Compare commits

...

23 Commits

Author SHA1 Message Date
Valerio Ageno 90da869277 feat: update version to v0.15.1 2024-12-07 11:14:13 +01:00
Valerio Ageno 80c4affaa8 feat: add cookies to the response (#201) 2024-12-07 11:10:47 +01:00
Marco Pasqualetti aa087193fe fix(crates/tuono_lib_macros): remove Cargo.tml warnings (#200) 2024-12-06 19:00:54 +01:00
Marco Pasqualetti af7e51f69b chore(ci): setup rust consistently across all workflows (#198) 2024-12-06 14:05:03 +01:00
Marco Pasqualetti 1b62d8ddc2 refactor(crates/tuono): build - ssg - disable zombie_processes warning and use TUONO_PORT to generate server URL (#199) 2024-12-06 13:05:24 +01:00
Valerio Ageno 0839579733 fix: documentation burger menu 2024-12-03 19:38:58 +01:00
Nayan Radadiya 7f9bf08f76 Add ️ as default favicon for documentation and examples (#191) (#192) 2024-12-03 19:35:25 +01:00
Valerio Ageno 79ec1c6922 feat: remove home page from documentation website (#187)
Co-authored-by: Valerio Ageno <valerio.ageno@qonto.com>
2024-12-03 16:29:30 +01:00
Valerio Ageno c68246df83 chore: update contributing documentation (#194) 2024-12-03 16:08:49 +01:00
Marco Pasqualetti abc0ad7120 chore(github actions): refine on paths (#189) 2024-12-03 15:59:10 +01:00
Valerio Ageno 4356ca1cff chore: update CONTRIBUTING.md 2024-12-03 14:03:41 +01:00
Valerio Ageno d6215d009c Update health_check.rs 2024-12-03 09:51:42 +01:00
Marco Pasqualetti 0b501e95a5 chore(apps/documentation): migrate to eslint 9 (#188) 2024-12-01 16:40:58 +01:00
Marco Pasqualetti f157ff37e0 refactor(packages/router): removed unused code (#184) 2024-12-01 11:03:12 +01:00
Marco Pasqualetti 11d425faa9 chore(deps-dev): update eslint to v9 (#147) 2024-12-01 10:07:35 +01:00
Valerio Ageno 5fc68a1926 feat: update documentation to v0.15.0 and fix aliases (#182)
Co-authored-by: Valerio Ageno <valerio.ageno@qonto.com>
2024-11-30 21:37:02 +01:00
Valerio Ageno ec4577c187 feat: add tuono.config.ts support (#153)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2024-11-30 19:58:04 +01:00
Valerio Ageno 8052de8ad5 feat: update version to v0.15.0 (#181) 2024-11-30 19:03:01 +01:00
Valerio Ageno 2fda959cd6 feat: update tuono new repository reference (#178)
Co-authored-by: Valerio Ageno <valerio.ageno@qonto.com>
2024-11-30 14:16:18 +01:00
Valerio Ageno 05fa8a5ae2 feat: update docs and readme repo links (#180) 2024-11-30 14:16:10 +01:00
Valerio Ageno a341b41e9a chore: update repo reference withing manifests (#179)
Co-authored-by: Valerio Ageno <valerio.ageno@qonto.com>
2024-11-30 14:15:36 +01:00
Valerio Ageno a2e0a4d78a feat: update version to v0.14.6 2024-11-30 14:13:21 +01:00
Valerio Ageno 4fb1d6077c Add tuono cli integration test (#177)
Co-authored-by: KingTheSim <KingTheSim@abv.bg>
Co-authored-by: KingTheSim <122887037+KingTheSim@users.noreply.github.com>
Co-authored-by: Valerio Ageno <valerio.ageno@qonto.com>
2024-11-29 16:34:56 +01:00
136 changed files with 1587 additions and 1173 deletions
-106
View File
@@ -1,106 +0,0 @@
{
"root": true,
"reportUnusedDisableDirectives": true,
"ignorePatterns": ["**/build", "**/dist"],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "import"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/stylistic",
"plugin:import/recommended",
"plugin:import/typescript",
"prettier",
],
"env": {
"browser": true,
"es2020": true,
},
"parserOptions": {
"tsconfigRootDir": ".",
"project": true,
"sourceType": "module",
"ecmaVersion": 2020,
},
"settings": {
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".tsx"],
},
"import/resolver": {
"typescript": true,
},
"react": {
"version": "detect",
},
},
"rules": {
"@typescript-eslint/array-type": "error",
"@typescript-eslint/no-wrapper-object-types": "error",
"@typescript-eslint/no-empty-object-type": "error",
"@typescript-eslint/no-unsafe-function-type": "error",
"@typescript-eslint/ban-ts-comment": "error",
"@typescript-eslint/consistent-type-definitions": "error",
"@typescript-eslint/consistent-type-imports": [
"error",
{ "prefer": "type-imports" },
],
"@typescript-eslint/explicit-module-boundary-types": "error",
"@typescript-eslint/method-signature-style": ["error", "property"],
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "typeParameter",
"format": ["PascalCase"],
"leadingUnderscore": "forbid",
"trailingUnderscore": "forbid",
"custom": {
"regex": "^(T|T[A-Z][A-Za-z]+)$",
"match": true,
},
},
],
"@typescript-eslint/no-deprecated": "error",
"@typescript-eslint/no-empty-function": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-non-null-assertion": "error",
"@typescript-eslint/no-unnecessary-condition": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-function-return-type": "error",
"@typescript-eslint/no-inferrable-types": [
"error",
{ "ignoreParameters": true },
],
"import/default": "error",
"import/export": "error",
"import/namespace": "error",
"import/newline-after-import": "error",
"import/no-cycle": "error",
"import/no-duplicates": "off",
"import/no-named-as-default-member": "error",
"import/no-unused-modules": "error",
"import/order": [
"off",
{
"groups": [
"builtin",
"external",
"internal",
"parent",
"sibling",
"index",
"object",
"type",
],
},
],
"no-case-declarations": "error",
"no-empty": "error",
"no-prototype-builtins": "error",
"no-redeclare": "error",
"no-shadow": "error",
"no-undef": "off",
"sort-imports": "off",
},
}
+10 -2
View File
@@ -3,13 +3,21 @@ name: Documentation Website CI
on:
push:
branches:
- main
- 'main'
paths:
# ⬇ Keep paths in sync with `typescript.yml`
- '.github/**'
- '*'
- '!Cargo.toml'
# ⬆ Keep paths in sync with `typescript.yml`
- 'apps/documentation/**'
pull_request:
paths:
# ⬇ Keep paths in sync with `typescript.yml`
- '.github/**'
- '*'
- '!Cargo.toml'
# ⬆ Keep paths in sync with `typescript.yml`
- 'apps/documentation/**'
jobs:
@@ -29,7 +37,7 @@ jobs:
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install tuono
run: cargo install tuono@0.12.3
run: cargo install tuono@0.15.0
- name: Build project
working-directory: ./apps/documentation
+2 -2
View File
@@ -3,7 +3,7 @@ name: Deploy documentation website on AWS S3
on:
push:
branches:
- main
- 'main'
paths:
- '.github/**'
- 'apps/documentation/**'
@@ -27,7 +27,7 @@ jobs:
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install tuono
run: cargo install tuono@0.12.3
run: cargo install tuono@0.15.0
- name: Build project
working-directory: ./apps/documentation
+6 -6
View File
@@ -34,10 +34,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
- name: Setup rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
override: true
toolchain: 'stable'
- uses: katyo/publish-crates@v2
with:
@@ -80,10 +80,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
- name: Setup rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
override: true
toolchain: 'stable'
- uses: katyo/publish-crates@v2
with:
+4 -3
View File
@@ -1,4 +1,5 @@
name: Repo root CI
# All workflows are triggered based on specific paths,
# the root repository is something that is not applicable to rust nor typescript
# so it has been moved in a standalone workflow
@@ -6,14 +7,14 @@ name: Repo root CI
on:
push:
branches:
- main
- 'main'
paths:
- '.github/**'
- './*'
- '*'
pull_request:
paths:
- '.github/**'
- './*'
- '*'
jobs:
build-and-test:
+11 -2
View File
@@ -3,13 +3,15 @@ name: Rust CI
on:
push:
branches:
- main
- 'main'
paths:
- '.github/**'
- 'Cargo.toml'
- 'crates/**'
pull_request:
paths:
- '.github/**'
- 'Cargo.toml'
- 'crates/**'
env:
@@ -43,7 +45,11 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- name: Setup rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: ${{ matrix.toolchain }}
- run: cargo build --verbose
- run: cargo test --verbose
@@ -55,5 +61,8 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
- run: cargo fmt --all -- --check
- run: cargo clippy -- -D warnings
+1 -1
View File
@@ -3,7 +3,7 @@ name: Spell Checker
on:
push:
branches:
- main
- 'main'
pull_request:
+9 -1
View File
@@ -3,13 +3,21 @@ name: Typescript CI
on:
push:
branches:
- main
- 'main'
paths:
# ⬇ Keep paths in sync with `ci-documentation.yml`
- '.github/**'
- '*'
- '!Cargo.toml'
# ⬆ Keep paths in sync with `ci-documentation.yml`
- 'packages/**'
pull_request:
paths:
# ⬇ Keep paths in sync with `ci-documentation.yml`
- '.github/**'
- '*'
- '!Cargo.toml'
# ⬆ Keep paths in sync with `ci-documentation.yml`
- 'packages/**'
jobs:
+3
View File
@@ -24,3 +24,6 @@ examples/*/pnpm-lock.yaml
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# Files, generated by the code editors
.vscode/
+10 -6
View File
@@ -1,5 +1,5 @@
![](https://github.com/Valerioageno/tuono/actions/workflows/rust.yml/badge.svg)
![](https://github.com/Valerioageno/tuono/actions/workflows/typescript.yml/badge.svg)
![](https://github.com/tuono-labs/tuono/actions/workflows/rust.yml/badge.svg)
![](https://github.com/tuono-labs/tuono/actions/workflows/typescript.yml/badge.svg)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
# How to Contribute to Tuono
@@ -7,15 +7,19 @@
## Contributions
Any feature contribution or suggestion is strongly appreciated.
Since the current project size, there isn't yet a defined way to start a discussion. Consider [opening a new issue](https://github.com/Valerioageno/tuono/issues/new/choose)
or to reach me using my email address, [valerioageno@yahoo.it](mailto:valerioageno@ahoo.it). I'm also available on Twitter (X); DM @valerioageno.
We are currently managing the tasks' prioritization in the [Road to V1](https://github.com/orgs/tuono-labs/projects/2) project. You can pick any task that you see in the `ready` column that is not already assigned.
If you want to contribute on something that is in the `backlog` column, or it hasn't been scoped yet, please drop a message in the [#general](https://discord.com/invite/khQzPa654B) discord channel so we can all have visibility of it.
Consider [opening a new issue](https://github.com/tuono-labs/tuono/issues/new/choose) if you find a bug.
## Bugs
**Did you find a bug?**
- Ensure the bug was not already reported by searching on GitHub under [Issues](https://github.com/Valerioageno/tuono/issues).
- If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/Valerioageno/tuono/issues/new/choose). Be sure to include a title and clear description, as much relevant information as possible, and a code sample or an executable test case demonstrating the expected behavior that is not occurring.
- Ensure the bug was not already reported by searching on GitHub under [Issues](https://github.com/tuono-labs/tuono/issues).
- If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/tuono-labs/tuono/issues/new/choose). Be sure to include a title and clear description, as much relevant information as possible, and a code sample or an executable test case demonstrating the expected behavior that is not occurring.
**Did you write a patch that fixes a bug?**
+1 -1
View File
@@ -7,7 +7,7 @@ members = [
]
exclude = [
"apps/documentation",
"examples/wit-mdx",
"examples/with-mdx",
"examples/tuono-app",
"examples/tuono-tutorial",
"benches/tuono"
+5 -5
View File
@@ -3,10 +3,10 @@
# Tuono
<img align="right" src="https://raw.githubusercontent.com/Valerioageno/tuono/main/assets/logo.png" width="160px">
<img align="right" src="https://raw.githubusercontent.com/tuono-labs/tuono/main/assets/logo.png" width="160px">
![](https://github.com/Valerioageno/tuono/actions/workflows/rust.yml/badge.svg)
![](https://github.com/Valerioageno/tuono/actions/workflows/typescript.yml/badge.svg)
![](https://github.com/tuono-labs/tuono/actions/workflows/rust.yml/badge.svg)
![](https://github.com/tuono-labs/tuono/actions/workflows/typescript.yml/badge.svg)
**Tuono is a full-stack web framework for building React applications using Rust as the backend with
a strong focus on usability and performance.**
@@ -48,7 +48,7 @@ by Tuono based on the files defined within the `./src/routes` directory.
The Tuono API tries to stick as much as possible to the Next.js one (or at least takes a huge inspiration
from it). The major difference is the backend system. While Next.js relies entirely on Node/Deno/Bun,
Tuono runs the server without any intermediary runtime. This enables impressive performance improvements
(check the benchmarks [here](https://github.com/Valerioageno/tuono/tree/main/benches)).
(check the benchmarks [here](https://github.com/tuono-labs/tuono/tree/main/benches)).
## Getting started
@@ -61,7 +61,7 @@ Then run `cargo install tuono`.
To list all the available commands, run `tuono -h`
To create a new project, run `tuono new [NAME]` (optionally, you can pass the `--template` (or `-t`) flag - check the
[examples](https://github.com/Valerioageno/tuono/tree/main/examples) folder).
[examples](https://github.com/tuono-labs/tuono/tree/main/examples) folder).
Then, to run the local development environment, install the JS dependencies (`npm install`) and run inside the project folder `tuono dev`
-106
View File
@@ -1,106 +0,0 @@
{
"root": true,
"reportUnusedDisableDirectives": true,
"ignorePatterns": ["**/build", "**/dist"],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "import"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/stylistic",
"plugin:import/recommended",
"plugin:import/typescript",
"prettier",
],
"env": {
"browser": true,
"es2020": true,
},
"parserOptions": {
"tsconfigRootDir": ".",
"project": true,
"sourceType": "module",
"ecmaVersion": 2020,
},
"settings": {
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".tsx"],
},
"import/resolver": {
"typescript": true,
},
"react": {
"version": "detect",
},
},
"rules": {
"@typescript-eslint/array-type": "error",
"@typescript-eslint/no-wrapper-object-types": "error",
"@typescript-eslint/no-empty-object-type": "error",
"@typescript-eslint/no-unsafe-function-type": "error",
"@typescript-eslint/ban-ts-comment": "error",
"@typescript-eslint/consistent-type-definitions": "error",
"@typescript-eslint/consistent-type-imports": [
"error",
{ "prefer": "type-imports" },
],
"@typescript-eslint/explicit-module-boundary-types": "error",
"@typescript-eslint/method-signature-style": ["error", "property"],
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "typeParameter",
"format": ["PascalCase"],
"leadingUnderscore": "forbid",
"trailingUnderscore": "forbid",
"custom": {
"regex": "^(T|T[A-Z][A-Za-z]+)$",
"match": true,
},
},
],
"@typescript-eslint/no-deprecated": "error",
"@typescript-eslint/no-empty-function": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-non-null-assertion": "error",
"@typescript-eslint/no-unnecessary-condition": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-function-return-type": "error",
"@typescript-eslint/no-inferrable-types": [
"error",
{ "ignoreParameters": true },
],
"import/default": "error",
"import/export": "error",
"import/namespace": "error",
"import/newline-after-import": "error",
"import/no-cycle": "error",
"import/no-duplicates": "off",
"import/no-named-as-default-member": "error",
"import/no-unused-modules": "error",
"import/order": [
"off",
{
"groups": [
"builtin",
"external",
"internal",
"parent",
"sibling",
"index",
"object",
"type",
],
},
],
"no-case-declarations": "error",
"no-empty": "error",
"no-prototype-builtins": "error",
"no-redeclare": "error",
"no-shadow": "error",
"no-undef": "off",
"sort-imports": "off",
},
}
+1 -1
View File
@@ -8,7 +8,7 @@ name = "tuono"
path = ".tuono/main.rs"
[dependencies]
tuono_lib = "0.12.3"
tuono_lib = "0.15.0"
glob = "0.3.1"
time = { version = "0.3", features = ["macros"] }
serde = { version = "1.0.202", features = ["derive"] }
+3 -2
View File
@@ -3,8 +3,9 @@
"description": "The react/rust fullstack framework documentation",
"version": "0.0.1",
"private": true,
"type": "module",
"scripts": {
"lint": "eslint --ext .ts,.tsx ./src -c .eslintrc",
"lint": "eslint .",
"format": "prettier -u --write --ignore-unknown './src/**/*'",
"format:check": "prettier --check --ignore-unknown './src/**/*'",
"types": "tsc --noEmit"
@@ -18,7 +19,7 @@
"clsx": "^2.1.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"tuono": "npm:tuono@0.12.3"
"tuono": "npm:tuono@0.15.0"
},
"devDependencies": {
"@types/mdx": "^2.0.13",
@@ -1,4 +1,4 @@
module.exports = {
export default {
plugins: {
'postcss-preset-mantine': {},
'postcss-simple-vars': {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 709 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

@@ -28,12 +28,6 @@ export default function TuonoBreadcrumbs({
"position": 1,
"name": "Tuono - The React/Rust fullstack framework",
"item": "https://tuono.dev"
},
{
"@type": "ListItem",
"position": 2,
"name": "Tuono - Documentation",
"item": "https://tuono.dev/documentation"
}${breadcrumbs.length > 0 ? ',' : ''}
${breadcrumbs
.map((br, i) =>
@@ -54,13 +48,7 @@ export default function TuonoBreadcrumbs({
mb="md"
mt="md"
>
<Button
href="/documentation"
component={Link}
variant="subtle"
radius="xl"
p={5}
>
<Button href="/" component={Link} variant="subtle" radius="xl" p={5}>
<IconBolt />
</Button>
{breadcrumbs.map((br) => (
@@ -4,7 +4,7 @@ import { IconEdit } from '@tabler/icons-react'
import { useRouter } from 'tuono'
const GITHUB_URL =
'https://github.com/Valerioageno/tuono/tree/main/apps/documentation/src/routes'
'https://github.com/tuono-labs/tuono/tree/main/apps/documentation/src/routes'
export default function EditPage(): JSX.Element {
const { pathname } = useRouter()
@@ -1,6 +1,5 @@
import type { JSX } from 'react'
import type { JSX, HTMLAttributes } from 'react'
import { Code } from '@mantine/core'
import type { HTMLAttributes } from 'react'
export default function MdxCode(
props: HTMLAttributes<HTMLPreElement>,
@@ -1,10 +1,17 @@
import type { JSX } from 'react'
import { CodeHighlight } from '@mantine/code-highlight'
import styles from './mdx-pre.module.css'
interface PreProps {
children: any
children: {
props: {
children: string
className?: string
}
}
}
export default function MdxPre({ children }: PreProps): JSX.Element {
return (
<CodeHighlight
@@ -1,8 +1,7 @@
import type { JSX } from 'react'
import type { JSX, ReactNode } from 'react'
import { MDXProvider } from '@mdx-js/react'
import MdxLink from './mdx-link'
import type { ReactNode } from 'react'
import MdxPre from './mdx-pre'
import MdxQuote from './mdx-quote'
import MdxCode from './mdx-code'
@@ -8,6 +8,7 @@ export default function MdxTitle(props: TitleProps): JSX.Element {
data-order={props.order}
mt={20}
{...props}
// eslint-disable-next-line @typescript-eslint/no-base-to-string
id={String(props.children ?? '')
.toLowerCase()
.replaceAll(' ', '-')}
@@ -1,11 +1,6 @@
import type { JSX } from 'react'
import { Flex, Button, ActionIcon, Group } from '@mantine/core'
import {
IconBrandGithub,
IconBook,
IconBrandDiscord,
} from '@tabler/icons-react'
import { Link } from 'tuono'
import { Flex, ActionIcon, Group } from '@mantine/core'
import { IconBrandGithub, IconBrandDiscord } from '@tabler/icons-react'
import ThemeBtn from '../theme-btn'
@@ -13,21 +8,11 @@ export default function Actions(): JSX.Element {
return (
<Flex gap={8}>
<Group gap={8}>
<Button
component={Link}
href="/documentation"
visibleFrom="sm"
size="compact-lg"
rightSection={<IconBook />}
autoContrast
>
Get started
</Button>
<ActionIcon
variant="default"
size="lg"
aria-label="Check the project on github"
href="https://github.com/Valerioageno/tuono"
href="https://github.com/tuono-labs/tuono"
target="_blank"
component="a"
>
@@ -1,6 +1,6 @@
import type { JSX } from 'react'
import { AppShell, Burger, Button, Flex } from '@mantine/core'
import { Link, useRouter } from 'tuono'
import { Link } from 'tuono'
import Actions from './actions'
@@ -10,7 +10,6 @@ interface NavbarProps {
}
export default function Navbar({ opened, toggle }: NavbarProps): JSX.Element {
const { pathname } = useRouter()
return (
<AppShell.Header p="sm">
<Flex justify="space-between">
@@ -19,14 +18,7 @@ export default function Navbar({ opened, toggle }: NavbarProps): JSX.Element {
</Button>
<Flex align="center" gap={8}>
<Actions />
{pathname.startsWith('/documentation') && (
<Burger
opened={opened}
onClick={toggle}
hiddenFrom="sm"
size="sm"
/>
)}
<Burger opened={opened} onClick={toggle} hiddenFrom="sm" size="sm" />
</Flex>
</Flex>
</AppShell.Header>
@@ -10,11 +10,7 @@ interface SidebarProps {
export default function Sidebar({ close }: SidebarProps): JSX.Element {
return (
<AppShell.Navbar p="md">
<SidebarLink
href="/documentation"
label="Getting started"
onClick={close}
/>
<SidebarLink href="/" label="Home" onClick={close} />
<SidebarLink
href="/documentation/installation"
label="Installation"
@@ -6,6 +6,7 @@ import { useEffect, useRef, useState } from 'react'
import { useRouter, Link } from 'tuono'
import { IconList } from '@tabler/icons-react'
import { Box, rem, ScrollArea, Text } from '@mantine/core'
import { getHeadings, type Heading } from './get-headings'
import classes from './table-of-content.module.css'
+4 -1
View File
@@ -1,2 +1,5 @@
// declaration.d.ts
declare module '*.css'
declare module '*.css' {
const CSSModule: Record<string, string>
export default CSSModule
}
+16 -22
View File
@@ -1,19 +1,23 @@
import type { ReactNode, JSX } from 'react'
import {
ColorSchemeScript,
createTheme,
MantineProvider,
AppShell,
Container,
} from '@mantine/core'
import { useDisclosure } from '@mantine/hooks'
import { Head, useRouter } from 'tuono'
import Navbar from '../components/navbar'
import { Head } from 'tuono'
import EditPage from '@/components/edit-page'
import MdxProvider from '@/components/mdx-provider'
import Navbar from '@/components/navbar'
import Sidebar from '@/components/sidebar'
import '@mantine/core/styles.css'
import '@mantine/code-highlight/styles.css'
import Sidebar from '../components/sidebar'
interface RootRouteProps {
children: ReactNode
}
@@ -61,29 +65,14 @@ const theme = createTheme({
export default function RootRoute({ children }: RootRouteProps): JSX.Element {
const [opened, { toggle }] = useDisclosure()
const { pathname } = useRouter()
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"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚡</text></svg>"
/>
<link rel="manifest" href="/site.webmanifest" />
</Head>
@@ -98,8 +87,13 @@ export default function RootRoute({ children }: RootRouteProps): JSX.Element {
}}
>
<Navbar opened={opened} toggle={toggle} />
{pathname.startsWith('/documentation') && <Sidebar close={toggle} />}
{children}
<Sidebar close={toggle} />
<AppShell.Main>
<Container id="mdx-root" component="article" size="md" p={20}>
<MdxProvider>{children}</MdxProvider>
<EditPage />
</Container>
</AppShell.Main>
</AppShell>
</MantineProvider>
</>
@@ -1,20 +0,0 @@
import type { ReactNode, JSX } from 'react'
import { AppShell, Container } from '@mantine/core'
import MdxProvider from '../../components/mdx-provider'
import EditPage from '../../components/edit-page'
interface RootRouteProps {
children: ReactNode
}
export default function RootRoute({ children }: RootRouteProps): JSX.Element {
return (
<AppShell.Main>
<Container id="mdx-root" component="article" size="md" p={20}>
<MdxProvider>{children}</MdxProvider>
<EditPage />
</Container>
</AppShell.Main>
)
}
@@ -1,4 +1,4 @@
import MetaTags from '../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Application state"
@@ -6,7 +6,7 @@ import MetaTags from '../../components/meta-tags'
description="Learn how to add features to your Tuono application"
/>
import Breadcrumbs, { Element } from '../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Application state' }]} />
@@ -1,4 +1,4 @@
import MetaTags from '../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - CLI"
@@ -6,7 +6,7 @@ import MetaTags from '../../components/meta-tags'
description="Tuono is the CLI that provides all the needed commands to handle the full-stack project."
/>
import Breadcrumbs, { Element } from '../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'CLI' }]} />
@@ -1,4 +1,4 @@
import MetaTags from '../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Contributing"
@@ -6,7 +6,7 @@ import MetaTags from '../../components/meta-tags'
description="The project is massive - if you like it, do consider contributing!"
/>
import Breadcrumbs, { Element } from '../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: '✨ Contributing' }]} />
@@ -1,4 +1,4 @@
import MetaTags from '../../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Contributing"
@@ -6,7 +6,7 @@ import MetaTags from '../../../components/meta-tags'
description="The project is massive - if you like it, do consider contributing!"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: '✨ Contributing' }]} />
@@ -28,11 +28,10 @@ The `tuono` project can mostly be split into the following subdomains:
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__`.
We are currently managing the tasks' prioritization in the [Road to V1](https://github.com/orgs/tuono-labs/projects/2) project.
You can pick any task that you see in the `ready` column that is not already assigned.
If you want to contribute on something that is in the `backlog` column, or it hasn't been scoped yet, please drop a message in the [#general](https://discord.com/invite/khQzPa654B) discord channel so we can all have visibility of it.
## Requirements
@@ -1,4 +1,4 @@
import MetaTags from '../../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Contributing - Local development"
@@ -6,7 +6,7 @@ import MetaTags from '../../../components/meta-tags'
description="Contribute to Tuono. Learn here how to setup the repository for local development"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs
breadcrumbs={[
@@ -1,6 +1,6 @@
import Breadcrumbs, { Element } from '../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
import MetaTags from '../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Installation"
@@ -1,11 +1,11 @@
import MetaTags from '../../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Routing"
canonical="https://tuono.dev/documentation/routing/intro"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Routing' }]} />
@@ -1,11 +1,11 @@
import MetaTags from '../../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Routing"
canonical="https://tuono.dev/documentation/routing"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs
breadcrumbs={[
@@ -1,4 +1,4 @@
import MetaTags from '../../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - API fetching"
@@ -6,7 +6,7 @@ import MetaTags from '../../../components/meta-tags'
description="Learn how to fetch remote APIs on the backend"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs
breadcrumbs={[
@@ -1,4 +1,4 @@
import MetaTags from '../../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Components"
@@ -6,7 +6,7 @@ import MetaTags from '../../../components/meta-tags'
description="Learn how to manage the components in a Tuono codebase"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs
breadcrumbs={[
@@ -1,4 +1,4 @@
import MetaTags from '../../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Conclusion"
@@ -6,7 +6,7 @@ import MetaTags from '../../../components/meta-tags'
description="You just created a multi thread full stack application with Rust and React"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs
breadcrumbs={[
@@ -1,4 +1,4 @@
import MetaTags from '../../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Development setup"
@@ -6,7 +6,7 @@ import MetaTags from '../../../components/meta-tags'
description="Learn how to setup a new Tuono project"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs
breadcrumbs={[
@@ -1,4 +1,4 @@
import MetaTags from '../../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Dynamic routes"
@@ -6,7 +6,7 @@ import MetaTags from '../../../components/meta-tags'
description="Learn how to catch more endpoint with the same route loading dynamic data"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs
breadcrumbs={[
@@ -1,4 +1,4 @@
import MetaTags from '../../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Error handling"
@@ -6,7 +6,7 @@ import MetaTags from '../../../components/meta-tags'
description="Learn how to handle the server side error on your Tuono project"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs
breadcrumbs={[
@@ -1,4 +1,4 @@
import MetaTags from '../../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Tutorial"
@@ -6,7 +6,7 @@ import MetaTags from '../../../components/meta-tags'
description="This tutorial is meant to give you a sneak peek at the framework and is intended to evolve during the development - be sure to have installed the latest version"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Tutorial' }]} />
@@ -1,4 +1,4 @@
import MetaTags from '../../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Building for production"
@@ -6,7 +6,7 @@ import MetaTags from '../../../components/meta-tags'
description="Learn how to build the production project and run the optimize version"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs
breadcrumbs={[
@@ -1,4 +1,4 @@
import MetaTags from '../../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Redirection"
@@ -6,7 +6,7 @@ import MetaTags from '../../../components/meta-tags'
description="Learn how to redirect to different route from the server"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs
breadcrumbs={[
@@ -1,4 +1,4 @@
import MetaTags from '../../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - SEO and meta tags"
@@ -6,7 +6,7 @@ import MetaTags from '../../../components/meta-tags'
description="Learn how to handle meaningful meta tags for your Tuono project"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs
breadcrumbs={[
@@ -1,12 +1,12 @@
import MetaTags from '../../components/meta-tags'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Documentation"
canonical="https://tuono.dev/documentation"
canonical="https://tuono.dev"
description="Tuono is a full-stack framework for building React applications using Rust as the backend."
/>
import Breadcrumbs from '../../components/breadcrumbs'
import Breadcrumbs from '@/components/breadcrumbs'
<Breadcrumbs />
@@ -36,7 +36,7 @@ Tuono addresses the following two limits:
#### NodeJs/Deno/Bun are the only runtimes that allow a React app to be server-side rendered (SSR)? (no)
Tuono is the first full-stack React framework with the server side written in Rust (check the [ssr-rs](https://github.com/Valerioageno/ssr-rs) crate).
Tuono is the first full-stack React framework with the server side written in Rust (check the [ssr-rs](https://github.com/tuono-labs/ssr-rs) crate).
Because of this, Tuono is extremely fast, and the requests are handled by a multithreaded Rust server.
React is still React - it is just superpowered.
@@ -51,4 +51,4 @@ The routing is handled by Tuono based on the files defined within the `./src/rou
The Tuono API tries to stick as much as possible to the Next.js one (or at least takes a huge inspiration
from it). The major difference is the backend system. While Next.js relies entirely on Node/Deno/Bun,
Tuono runs the server without any intermediary runtime. This enables impressive performance improvements
(check the benchmarks [here](https://github.com/Valerioageno/tuono/tree/main/benches)).
(check the benchmarks [here](https://github.com/tuono-labs/tuono/tree/main/benches)).
-17
View File
@@ -1,17 +0,0 @@
import type { JSX } from 'react'
import Hero from '../components/hero'
import MetaTags from '../components/meta-tags'
export default function IndexPage(): JSX.Element {
return (
<>
<MetaTags
title="Tuono - The React/Rust full-stack framework"
canonical="https://tuono.dev"
description="The technologies we love seamlessly working together to unleash the highest web performance ever met on React"
/>
<Hero />
</>
)
}
+20 -10
View File
@@ -1,8 +1,7 @@
use tuono_lib::reqwest::StatusCode;
use tuono_lib::{Request, Response};
use tuono_lib::axum::http::{header, HeaderMap};
use glob::glob;
use time::OffsetDateTime;
use tuono_lib::axum::http::{header, HeaderMap, StatusCode};
use tuono_lib::{Request, Response};
const FILE_TO_EXCLUDE: [&str; 2] = ["sitemap.xml", "__root"];
@@ -14,11 +13,17 @@ const SITEMAP: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
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() {
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)) {
if !FILE_TO_EXCLUDE
.iter()
.any(|exclude| path.ends_with(exclude))
{
paths.push(path)
}
}
@@ -51,11 +56,16 @@ async fn generate_sitemap(_req: Request) -> Response {
url.pop();
}
sitemaps.push_str(
&format!(r#"<url><loc>{}</loc><lastmod>{}</lastmod></url>"#,url, OffsetDateTime::now_utc().date())
)
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)))
Response::Custom((
StatusCode::OK,
headers,
SITEMAP.replace("[PLACEHOLDER]", &sitemaps),
))
}
+29 -17
View File
@@ -1,25 +1,37 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2021", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
// Typechecking
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,
// Modules
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"paths": {
"@/*": ["./src/*"]
},
// Emit
"noEmit": true,
// JavaScript Support
"allowJs": true,
// Interop Constraints
"isolatedModules": true,
// Language and Environment
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2021", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
// Completeness
"skipLibCheck": true
},
"include": ["src"],
"include": ["src", "tuono.config.ts", "postcss.config.js"],
"references": [{ "path": "./tsconfig.node.json" }]
}
+12
View File
@@ -0,0 +1,12 @@
import type { TuonoConfig } from 'tuono/config'
const config: TuonoConfig = {
vite: {
alias: {
'@': 'src',
'@tabler/icons-react': '@tabler/icons-react/dist/esm/icons/index.mjs',
},
},
}
export default config
+7 -2
View File
@@ -1,12 +1,12 @@
[package]
name = "tuono"
version = "0.14.5"
version = "0.15.1"
edition = "2021"
authors = ["V. Ageno <valerioageno@yahoo.it>"]
description = "Superfast React fullstack framework"
homepage = "https://tuono.dev"
keywords = [ "react", "typescript", "fullstack", "web", "ssr"]
repository = "https://github.com/Valerioageno/tuono"
repository = "https://github.com/tuono-labs/tuono"
readme = "../../README.md"
license-file = "../../LICENSE.md"
categories = ["web-programming"]
@@ -33,3 +33,8 @@ reqwest = {version = "0.12.4", features =["blocking", "json"]}
serde_json = "1.0"
fs_extra = "1.3.0"
http = "1.1.0"
[dev-dependencies]
tempfile = "3.14.0"
assert_cmd = "2.0.16"
serial_test = "0.4.0"
+13
View File
@@ -21,11 +21,17 @@ const ROUTES_FOLDER_PATH: &str = "\\src\\routes";
#[cfg(target_os = "windows")]
const BUILD_JS_SCRIPT: &str = ".\\node_modules\\.bin\\tuono-build-prod.cmd";
#[cfg(target_os = "windows")]
const BUILD_TUONO_CONFIG: &str = ".\\node_modules\\.bin\\tuono-build-config.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";
#[cfg(not(target_os = "windows"))]
const BUILD_TUONO_CONFIG: &str = "./node_modules/.bin/tuono-build-config";
#[derive(Debug)]
pub struct App {
pub route_map: HashMap<String, Route>,
@@ -147,6 +153,13 @@ impl App {
.expect("Failed to run the rust server")
}
pub fn build_tuono_config(&self) -> Result<std::process::Output, std::io::Error> {
Command::new(BUILD_TUONO_CONFIG)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
}
pub fn get_used_http_methods(&self) -> HashSet<Method> {
let mut acc = HashSet::new();
+10 -2
View File
@@ -87,7 +87,9 @@ pub fn app() -> std::io::Result<()> {
Actions::Dev => {
check_ports(Mode::Dev);
let _ = init_tuono_folder(Mode::Dev)?;
let app = init_tuono_folder(Mode::Dev)?;
app.build_tuono_config()
.expect("Failed to build tuono.config.ts");
watch::watch().unwrap();
}
@@ -105,6 +107,9 @@ pub fn app() -> std::io::Result<()> {
return Ok(());
}
app.build_tuono_config()
.expect("Failed to build tuono.config.ts");
app.build_react_prod();
if ssg {
@@ -128,6 +133,8 @@ pub fn app() -> std::io::Result<()> {
)
.expect("Failed to clone assets into static output folder");
// the process is killed below so we don't really need to use wait() function
#[allow(clippy::zombie_processes)]
let mut rust_server = app.run_rust_server();
let reqwest = reqwest::blocking::Client::builder()
@@ -139,7 +146,8 @@ pub fn app() -> std::io::Result<()> {
let mut is_server_ready = false;
while !is_server_ready {
if reqwest.get("http://localhost:3000").send().is_ok() {
let server_url = format!("http://localhost:{}", TUONO_PORT);
if reqwest.get(server_url).send().is_ok() {
is_server_ready = true
}
// TODO: add maximum tries
+3 -3
View File
@@ -7,9 +7,9 @@ use std::io::{self, prelude::*};
use std::path::{Path, PathBuf};
const GITHUB_TUONO_REPO_URL: &str =
"https://api.github.com/repos/Valerioageno/tuono/git/trees/main?recursive=1";
"https://api.github.com/repos/tuono-labs/tuono/git/trees/main?recursive=1";
const GITHUB_RAW_CONTENT_URL: &str = "https://raw.githubusercontent.com/Valerioageno/tuono/main/";
const GITHUB_RAW_CONTENT_URL: &str = "https://raw.githubusercontent.com/tuono-labs/tuono/main/";
#[derive(Deserialize, Debug)]
enum GithubFileType {
@@ -64,7 +64,7 @@ pub fn create_new_project(folder_name: Option<String>, template: Option<String>)
if new_project_files.is_empty() {
eprintln!("Error: Template '{template}' not found");
println!("Hint: you can view the available templates at https://github.com/Valerioageno/tuono/tree/main/examples");
println!("Hint: you can view the available templates at https://github.com/tuono-labs/tuono/tree/main/examples");
std::process::exit(1);
}
+32
View File
@@ -0,0 +1,32 @@
mod utils;
use assert_cmd::Command;
use serial_test::serial;
use std::fs;
use utils::TempTuonoProject;
#[test]
#[serial]
fn it_successfully_create_the_index_route() {
let temp_tuono_project = TempTuonoProject::new();
temp_tuono_project.add_route("./src/routes/index.rs");
let mut test_tuono_build = Command::cargo_bin("tuono").unwrap();
test_tuono_build
.arg("build")
.arg("--no-js-emit")
.assert()
.success();
let temp_main_rs_path = temp_tuono_project.path().join(".tuono/main.rs");
let temp_main_rs_content =
fs::read_to_string(&temp_main_rs_path).expect("Failed to read '.tuono/main.rs' content.");
assert!(temp_main_rs_content.contains(r#"#[path="../src/routes/index.rs"]"#));
assert!(temp_main_rs_content.contains("mod index;"));
assert!(temp_main_rs_content.contains(
r#".route("/", get(index::route)).route("/__tuono/data/data.json", get(index::api))"#
));
}
+47
View File
@@ -0,0 +1,47 @@
use fs_extra::dir::create_all;
use std::env;
use std::fs::File;
use std::path::{Path, PathBuf};
use tempfile::{tempdir, TempDir};
#[derive(Debug)]
pub struct TempTuonoProject {
original_dir: PathBuf,
temp_dir: TempDir,
}
impl TempTuonoProject {
pub fn new() -> Self {
let original_dir = env::current_dir().expect("Failed to read current_dir");
let temp_dir = tempdir().expect("Failed to create temp_dir");
env::set_current_dir(temp_dir.path()).expect("Failed to change current dir into temp_dir");
TempTuonoProject {
original_dir,
temp_dir,
}
}
pub fn path(&self) -> &Path {
self.temp_dir.path()
}
pub fn add_route<'a>(&self, path: &'a str) {
let path = PathBuf::from(path);
create_all(
path.parent().expect("Route path does not have any parent"),
false,
)
.expect("Failed to create parent route directory");
File::create(path).expect("Failed to create the route file");
}
}
impl Drop for TempTuonoProject {
fn drop(&mut self) {
// Set back the current dir in the previous state
env::set_current_dir(self.original_dir.to_owned())
.expect("Failed to restore the original directory.");
}
}
+4 -3
View File
@@ -1,12 +1,12 @@
[package]
name = "tuono_lib"
version = "0.14.5"
version = "0.15.1"
edition = "2021"
authors = ["V. Ageno <valerioageno@yahoo.it>"]
description = "Superfast React fullstack framework"
homepage = "https://tuono.dev"
keywords = [ "react", "typescript", "fullstack", "web", "ssr"]
repository = "https://github.com/Valerioageno/tuono"
repository = "https://github.com/tuono-labs/tuono"
readme = "../../README.md"
license-file = "../../LICENSE.md"
categories = ["web-programming"]
@@ -19,6 +19,7 @@ include = [
[dependencies]
ssr_rs = "0.7.0"
axum = {version = "0.7.5", features = ["json", "ws"]}
axum-extra = {version = "0.9.6", features = ["cookie"]}
tokio = { version = "1.37.0", features = ["full"] }
serde = { version = "1.0.202", features = ["derive"] }
erased-serde = "0.4.5"
@@ -31,7 +32,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.5"}
tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.15.1"}
# Match the same version used by axum
tokio-tungstenite = "0.24.0"
futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] }
+1
View File
@@ -19,4 +19,5 @@ pub use tuono_lib_macros::{api, handler};
// Re-exports
pub use axum;
pub use axum_extra::extract::cookie;
pub use tokio;
+64 -7
View File
@@ -3,11 +3,13 @@ use crate::{ssr::Js, Payload};
use axum::http::{HeaderMap, StatusCode};
use axum::response::{Html, IntoResponse, Redirect};
use axum::Json;
use axum_extra::extract::cookie::{Cookie, CookieJar};
use erased_serde::Serialize;
pub struct Props {
data: Box<dyn Serialize>,
http_code: StatusCode,
cookies: CookieJar,
}
pub enum Response {
@@ -57,26 +59,45 @@ impl Props {
Props {
data: Box::new(data),
http_code: StatusCode::OK,
cookies: CookieJar::new(),
}
}
pub fn status(&mut self, http_code: StatusCode) {
self.http_code = http_code;
}
pub fn new_with_status(data: impl Serialize + 'static, http_code: StatusCode) -> Self {
Props {
data: Box::new(data),
http_code,
cookies: CookieJar::new(),
}
}
pub fn add_cookie(&mut self, cookie: Cookie) {
let jar = self.cookies.clone().add(cookie.into_owned());
self.cookies = jar
}
}
impl Response {
pub fn render_to_string(&self, req: Request) -> impl IntoResponse {
match self {
Self::Props(Props { data, http_code }) => {
let payload = Payload::new(&req, data).client_payload().unwrap();
Self::Props(Props {
data,
http_code,
cookies,
}) => {
let payload = Payload::new(&req, data.as_ref()).client_payload().unwrap();
match Js::render_to_string(Some(&payload)) {
Ok(html) => (*http_code, Html(html)),
Err(_) => (*http_code, Html("500 Internal server error".to_string())),
Ok(html) => (*http_code, cookies.clone(), Html(html)),
Err(_) => (
*http_code,
cookies.clone(),
Html("500 Internal server error".to_string()),
),
}
.into_response()
}
@@ -87,9 +108,16 @@ impl Response {
pub fn json(&self) -> impl IntoResponse {
match self {
Self::Props(Props { data, http_code }) => {
(*http_code, Json(JsonResponse::new(data))).into_response()
}
Self::Props(Props {
data,
http_code,
cookies,
}) => (
*http_code,
cookies.clone(),
Json(JsonResponse::new(data.as_ref())),
)
.into_response(),
Self::Redirect(destination) => (
StatusCode::PERMANENT_REDIRECT,
Json(JsonResponse::new_redirect(destination.to_string())),
@@ -101,3 +129,32 @@ impl Response {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_update_the_props_status_and_cookie() {
let mut props = Props::new("{}");
props.status(StatusCode::NOT_FOUND);
props.add_cookie(Cookie::new("test", "cookie"));
assert_eq!(props.http_code, StatusCode::NOT_FOUND);
assert_eq!(
props.cookies.get("test").unwrap(),
&Cookie::new("test", "cookie")
);
}
#[test]
fn should_add_a_cookie_jar() {
let mut props = Props::new("{}");
props.status(StatusCode::NOT_FOUND);
props.add_cookie(Cookie::new("test", "cookie"));
assert_eq!(props.http_code, StatusCode::NOT_FOUND);
assert_eq!(
props.cookies.get("test").unwrap(),
&Cookie::new("test", "cookie")
);
}
}
+3 -4
View File
@@ -1,11 +1,11 @@
[package]
name = "tuono_lib_macros"
version = "0.14.5"
version = "0.15.1"
edition = "2021"
description = "Superfast React fullstack framework"
homepage = "https://tuono.dev"
keywords = [ "react", "typescript", "fullstack", "web", "ssr"]
repository = "https://github.com/Valerioageno/tuono"
repository = "https://github.com/tuono-labs/tuono"
readme = "../../README.md"
license-file = "../../LICENSE.md"
categories = ["web-programming"]
@@ -15,8 +15,7 @@ include = [
]
[lib]
crate_type = ["proc-macro"]
proc-marco = true
proc-macro = true
[dependencies]
syn = { version = "2.0.0", features = ["full"] }
+140
View File
@@ -0,0 +1,140 @@
import eslint from '@eslint/js'
import tseslint from 'typescript-eslint'
// @ts-expect-error no types are available for this plugin
import eslintPluginImport from 'eslint-plugin-import'
export default tseslint.config(
{
ignores: [
// #region shared
'**/build',
'**/dist',
'**/.tuono',
'**/vite.config.ts.timestamp**',
// #endregion shared
// #region package-specific
'packages/fs-router-vite-plugin/tests/generator/**',
'packages/tuono/bin/**',
// #endregion package-specific
],
},
{
linterOptions: {
reportUnusedDisableDirectives: 'error',
},
},
eslint.configs.recommended,
eslintPluginImport.flatConfigs.recommended,
eslintPluginImport.flatConfigs.typescript,
tseslint.configs.recommendedTypeChecked,
{
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
typescript: true,
},
react: {
version: 'detect',
},
},
rules: {
// #region @typescript-eslint
'@typescript-eslint/array-type': 'error',
'@typescript-eslint/no-wrapper-object-types': 'error',
'@typescript-eslint/no-empty-object-type': 'error',
'@typescript-eslint/no-unsafe-function-type': 'error',
'@typescript-eslint/ban-ts-comment': 'error',
'@typescript-eslint/consistent-type-definitions': 'error',
'@typescript-eslint/consistent-type-imports': [
'error',
{ prefer: 'type-imports' },
],
'@typescript-eslint/explicit-module-boundary-types': 'error',
'@typescript-eslint/method-signature-style': ['error', 'property'],
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'typeParameter',
format: ['PascalCase'],
leadingUnderscore: 'forbid',
trailingUnderscore: 'forbid',
custom: {
regex: '^(T|T[A-Z][A-Za-z]+)$',
match: true,
},
},
],
'@typescript-eslint/no-deprecated': 'error',
'@typescript-eslint/no-empty-function': 'error',
'@typescript-eslint/no-empty-interface': 'error',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'error',
'@typescript-eslint/no-unnecessary-condition': 'error',
'@typescript-eslint/no-unnecessary-type-assertion': 'error',
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/explicit-function-return-type': 'error',
'@typescript-eslint/no-inferrable-types': [
'error',
{ ignoreParameters: true },
],
// #endregion @typescript-eslint
// #region import
'import/default': 'error',
'import/export': 'error',
'import/namespace': 'error',
'import/newline-after-import': 'error',
'import/no-cycle': 'error',
'import/no-duplicates': 'error',
'import/no-named-as-default-member': 'error',
'import/no-unused-modules': 'error',
'import/order': [
'error',
{
'newlines-between': 'always-and-inside-groups',
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
'object',
],
},
],
// #endregion import
// #region misc
/**
* @todo some of this might be removed.
* They are handled by other plugin and / or typescript
*/
'no-case-declarations': 'error',
'no-empty': 'error',
'no-prototype-builtins': 'error',
'no-redeclare': 'error',
'no-shadow': 'error',
'no-undef': 'off',
'sort-imports': 'off',
},
},
{
files: ['apps/documentation/**'],
settings: {
'import/resolver': {
typescript: 'apps/documentation/tsconfig.json',
},
},
},
)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

+13 -1
View File
@@ -1,9 +1,21 @@
import type { ReactNode, JSX } from 'react'
import { Head } from 'tuono'
interface RootRouteProps {
children: ReactNode
}
export default function RootRoute({ children }: RootRouteProps): JSX.Element {
return <main className="main">{children}</main>
return (
<>
<Head>
<link
rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚡</text></svg>"
/>
<title>Tuono app</title>
</Head>
<main className="main">{children}</main>
</>
)
}
@@ -2,6 +2,6 @@ use tuono_lib::Request;
use tuono_lib::axum::http::StatusCode;
#[tuono_lib::api(GET)]
pub async fn my_get_request(_req: Request) -> StatusCode {
pub async fn health_check(_req: Request) -> StatusCode {
StatusCode::OK
}
+38 -38
View File
@@ -2,47 +2,47 @@ import type { JSX } from 'react'
import type { TuonoProps } from 'tuono'
interface IndexProps {
subtitle: string
subtitle: string
}
export default function IndexPage({
data,
isLoading,
data,
isLoading,
}: TuonoProps<IndexProps>): JSX.Element {
if (isLoading) {
return <h1>Loading...</h1>
}
if (isLoading) {
return <h1>Loading...</h1>
}
return (
<>
<header className="header">
<a href="https://crates.io/crates/tuono" target="_blank">
Crates
</a>
<a href="https://www.npmjs.com/package/tuono" target="_blank">
Npm
</a>
</header>
<div className="title-wrap">
<h1 className="title">
TU<span>O</span>NO
</h1>
<div className="logo">
<img src="rust.svg" className="rust" />
<img src="react.svg" className="react" />
</div>
</div>
<div className="subtitle-wrap">
<p className="subtitle">{data?.subtitle}</p>
<a
href="https://github.com/Valerioageno/tuono"
target="_blank"
className="button"
type="button"
>
Github
</a>
</div>
</>
)
return (
<>
<header className="header">
<a href="https://crates.io/crates/tuono" target="_blank">
Crates
</a>
<a href="https://www.npmjs.com/package/tuono" target="_blank">
Npm
</a>
</header>
<div className="title-wrap">
<h1 className="title">
TU<span>O</span>NO
</h1>
<div className="logo">
<img src="rust.svg" className="rust" />
<img src="react.svg" className="react" />
</div>
</div>
<div className="subtitle-wrap">
<p className="subtitle">{data?.subtitle}</p>
<a
href="https://github.com/tuono-labs/tuono"
target="_blank"
className="button"
type="button"
>
Github
</a>
</div>
</>
)
}
+1 -1
View File
@@ -20,6 +20,6 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"include": ["src", "tuono.config.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}
+5
View File
@@ -0,0 +1,5 @@
import type { TuonoConfig } from 'tuono/config'
const config: TuonoConfig = {}
export default config
Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

+13 -1
View File
@@ -1,9 +1,21 @@
import type { ReactNode, JSX } from 'react'
import { Head } from 'tuono'
interface RootRouteProps {
children: ReactNode
}
export default function RootRoute({ children }: RootRouteProps): JSX.Element {
return <main className="main">{children}</main>
return (
<>
<Head>
<link
rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚡</text></svg>"
/>
<title>Tuono tutorial</title>
</Head>
<main className="main">{children}</main>
</>
)
}
+1 -1
View File
@@ -2,7 +2,7 @@
import type { JSX } from 'react'
import { Head, type TuonoProps } from 'tuono'
import PokemonLink from '../components/PokemonLink'
import PokemonLink from '@/components/PokemonLink'
interface Pokemon {
name: string
@@ -1,7 +1,7 @@
import type { JSX } from 'react'
import { Head, type TuonoProps } from 'tuono'
import PokemonView from '../../components/PokemonView'
import PokemonView from '@/components/PokemonView'
interface Pokemon {
name: string
+5 -2
View File
@@ -18,8 +18,11 @@
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"],
"include": ["src", "tuono.config.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}
+11
View File
@@ -0,0 +1,11 @@
import type { TuonoConfig } from 'tuono/config'
const config: TuonoConfig = {
vite: {
alias: {
'@': 'src',
},
},
}
export default config
Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

+13 -3
View File
@@ -1,5 +1,6 @@
import type { ReactNode, JSX } from 'react'
import { MDXProvider } from '@mdx-js/react'
import { Head } from 'tuono'
interface RootRouteProps {
children: ReactNode
@@ -7,8 +8,17 @@ interface RootRouteProps {
export default function RootRoute({ children }: RootRouteProps): JSX.Element {
return (
<main className="main">
<MDXProvider components={{}}>{children}</MDXProvider>
</main>
<>
<Head>
<link
rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚡</text></svg>"
/>
<title>Tuono - With MDX</title>
</Head>
<main className="main">
<MDXProvider components={{}}>{children}</MDXProvider>
</main>
</>
)
}
+1 -1
View File
@@ -20,6 +20,6 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"include": ["src", "tuono.config.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}
+5
View File
@@ -0,0 +1,5 @@
import type { TuonoConfig } from 'tuono/config'
const config: TuonoConfig = {}
export default config
+8 -9
View File
@@ -1,5 +1,7 @@
{
"name": "workspace",
"private": true,
"type": "module",
"packageManager": "pnpm@9.14.2+sha512.6e2baf77d06b9362294152c851c4f278ede37ab1eba3a55fda317a4a17b209f4dbb973fb250a77abc463a341fcb1f17f17cfa24091c4eb319cda0d9b84278387",
"scripts": {
"dev": "turbo dev --filter=./packages/*",
@@ -21,24 +23,21 @@
"author": "Valerio Ageno",
"license": "MIT",
"devDependencies": {
"@eslint/js": "9.15.0",
"@tanstack/config": "^0.7.0",
"@types/node": "^22.0.0",
"@types/react": "^18.3.1",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint": "^9.15.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^5.0.0",
"prettier": "^3.2.4",
"typescript": "^5.4.5",
"turbo": "^2.2.3",
"typescript": "^5.6.3",
"typescript-eslint": "8.15.0",
"vite": "^5.2.11",
"vitest": "^2.0.0"
},
"dependencies": {
"turbo": "^2.2.3"
}
}
+2 -2
View File
@@ -1,10 +1,10 @@
# tuono-fs-router-vite-plugin
This is a vite plugin for [tuono](https://github.com/Valerioageno/tuono).
This is a vite plugin for [tuono](https://github.com/tuono-labs/tuono).
This package specifically handles the file system based routing.
Check [tuono](https://github.com/Valerioageno/tuono) for more.
Check [tuono](https://github.com/tuono-labs/tuono) for more.
## Credits
+7 -2
View File
@@ -1,18 +1,23 @@
{
"name": "tuono-fs-router-vite-plugin",
"version": "0.14.5",
"version": "0.15.1",
"description": "Plugin for the tuono's file system router. Tuono is the react/rust fullstack framework",
"homepage": "https://tuono.dev",
"scripts": {
"dev": "vite build --watch",
"build": "vite build",
"lint": "eslint --ext .ts,.tsx ./src -c ../../.eslintrc",
"lint": "eslint .",
"format": "prettier -u --write --ignore-unknown '**/*'",
"format:check": "prettier --check --ignore-unknown '**/*'",
"types": "tsc --noEmit",
"test:watch": "vitest",
"test": "vitest run"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tuono-labs/tuono.git",
"directory": "packages/fs-router-vite-plugin"
},
"keywords": [],
"author": "Valerio Ageno",
"license": "MIT",
@@ -1,4 +1,5 @@
import { describe, it, expect } from 'vitest'
import { buildRouteConfig } from './build-route-config'
const routes = [
@@ -61,11 +62,11 @@ const routes = [
},
]
describe('buildRouteConfig works', async () => {
describe('buildRouteConfig works', () => {
it('Should build the correct config', () => {
const expectedConfig =
'PostsMyPostRoute,PostsIndexRoute,PostspostIndexRoute'
const config = buildRouteConfig(routes)
expect(config).toStrictEqual(expectedConfig)
expect(config).toStrictEqual(
'PostsMyPostRoute,PostsIndexRoute,PostspostIndexRoute',
)
})
})
@@ -1,8 +1,10 @@
import * as fsp from 'fs/promises'
import path from 'path'
import { format } from 'prettier'
import { buildRouteConfig } from './build-route-config'
import { hasParentRoute } from './has-parent-route'
import {
cleanPath,
determineNodePath,
@@ -19,7 +21,6 @@ import {
import type { Config, RouteNode } from './types'
import { ROUTES_FOLDER, ROOT_PATH_ID, GENERATED_ROUTE_TREE } from './constants'
import { format } from 'prettier'
import { sortRouteNodes } from './sort-route-nodes'
import isDefaultExported from './utils/is-default-exported'
@@ -130,7 +131,7 @@ export async function routeGenerator(config = defaultConfig): Promise<void> {
// Loop over the flat list of routeNodes and
// build up a tree based on the routeNodes' routePath
const handleNode = async (node: RouteNode): Promise<void> => {
const handleNode = (node: RouteNode): void => {
const parentRoute = hasParentRoute(routeNodes, node, node.routePath)
if (parentRoute) node.parent = parentRoute
@@ -149,7 +150,7 @@ export async function routeGenerator(config = defaultConfig): Promise<void> {
}
for (const node of preRouteNodes) {
await handleNode(node)
handleNode(node)
}
const routeConfigChildrenText = buildRouteConfig(routeNodes)
@@ -237,7 +238,8 @@ export async function routeGenerator(config = defaultConfig): Promise<void> {
const routeTreeContent = await fsp
.readFile(path.resolve(config.generatedRouteTree), 'utf-8')
.catch((err: any) => {
.catch((e) => {
const err = e as Error & { code?: string }
if (err.code === 'ENOENT') {
return undefined
}
@@ -1,4 +1,5 @@
import { describe, it, expect } from 'vitest'
import { hasParentRoute } from './has-parent-route'
const routes = [
@@ -70,7 +71,7 @@ const dynamicRoute = {
cleanedPath: '/posts/[post]',
}
describe('hasParentRoute works', async () => {
describe('hasParentRoute works', () => {
it('Should detect parent route', () => {
const parentRoute = hasParentRoute(routes, myPost, myPost.path)
expect(parentRoute).toStrictEqual(parent)
+2 -2
View File
@@ -1,9 +1,9 @@
import { routeGenerator } from './generator'
import { normalize } from 'path'
import type { Plugin } from 'vite'
import { routeGenerator } from './generator'
const ROUTES_DIRECTORY_PATH = './src/routes'
let lock = false
@@ -1,4 +1,5 @@
import { describe, it, expect } from 'vitest'
import { sortRouteNodes } from './sort-route-nodes'
const routes = [
@@ -106,7 +107,7 @@ const expectedSorting = [
},
]
describe('sortRouteNodes works', async () => {
describe('sortRouteNodes works', () => {
it('Should correctly sort the nodes', () => {
const sorted = sortRouteNodes(routes)
expect(sorted).toStrictEqual(expectedSorting)
+6 -5
View File
@@ -35,7 +35,7 @@ export function routePathToVariable(routePath: string): string {
export function multiSortBy<T>(
arr: T[],
accessors: ((item: T) => any)[] = [(d): any => d],
accessors: ((item: T) => unknown)[] = [(d): unknown => d],
): T[] {
return arr
.map((d, i) => [d, i] as const)
@@ -55,7 +55,7 @@ export function multiSortBy<T>(
continue
}
return ao > bo ? 1 : -1
return (ao as number) > (bo as number) ? 1 : -1
}
return ai - bi
@@ -81,8 +81,8 @@ export function removeTrailingUnderscores(s?: string): string | undefined {
/**
* Removes all segments from a given path that start with an underscore ('_').
*
* @param {string} routePath - The path from which to remove segments. Defaults to '/'.
* @returns {string} The path with all underscore-prefixed segments removed.
* @param routePath - The path from which to remove segments. Defaults to '/'.
* @returns The path with all underscore-prefixed segments removed.
* @example
* removeLayoutSegments('/workspace/_auth/foo') // '/workspace/foo'
*/
@@ -101,8 +101,9 @@ export function trimPathLeft(pathToTrim: string): string {
}
export function removeLastSlash(str: string): string {
if (str.length > 1 && str.endsWith('/'))
if (str.length > 1 && str.endsWith('/')) {
return str.substring(0, str.length - 1)
}
return str
}
@@ -1,5 +1,7 @@
import fs from 'fs/promises'
import { describe, it, expect } from 'vitest'
import { routeGenerator } from '../src/generator'
describe('generator works', async () => {
+2 -1
View File
@@ -1,4 +1,5 @@
{
"extends": "../../tsconfig.json",
"include": ["src", "vite.config.ts"]
"include": ["src", "tests", "vite.config.ts"],
"exclude": ["tests/generator/**"]
}
+2 -2
View File
@@ -1,8 +1,8 @@
# tuono-lazy-fn-vite-plugin
This is a vite plugin for [tuono](https://github.com/Valerioageno/tuono).
This is a vite plugin for [tuono](https://github.com/tuono-labs/tuono).
This package specifically handles the transpiling of the `dynamic` function
allowing custom componenents to be lazy loaded but also server side rendered.
Check [tuono](https://github.com/Valerioageno/tuono) for more.
Check [tuono](https://github.com/tuono-labs/tuono) for more.
+7 -2
View File
@@ -1,18 +1,23 @@
{
"name": "tuono-lazy-fn-vite-plugin",
"version": "0.14.5",
"version": "0.15.1",
"description": "Plugin for the tuono's lazy fn. Tuono is the react/rust fullstack framework",
"homepage": "https://tuono.dev",
"scripts": {
"dev": "vite build --watch",
"build": "vite build",
"lint": "eslint --ext .ts,.tsx ./src -c ../../.eslintrc",
"lint": "eslint .",
"format": "prettier -u --write --ignore-unknown '**/*'",
"format:check": "prettier --check --ignore-unknown '**/*'",
"types": "tsc --noEmit",
"test:watch": "vitest",
"test": "vitest run"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tuono-labs/tuono.git",
"directory": "packages/lazy-fn-vite-plugin"
},
"keywords": [],
"author": "Valerio Ageno",
"license": "MIT",
+9 -11
View File
@@ -1,16 +1,7 @@
import type { Plugin } from 'vite'
import * as babel from '@babel/core'
import type { PluginItem } from '@babel/core'
import { isTuonoDynamicFnImported } from './utils'
import {
TUONO_MAIN_PACKAGE,
TUONO_DYNAMIC_FN_ID,
TUONO_LAZY_FN_ID,
} from './constants'
import * as t from '@babel/types'
import type { Plugin } from 'vite'
import type { PluginItem } from '@babel/core'
import type {
Identifier,
CallExpression,
@@ -18,6 +9,13 @@ import type {
StringLiteral,
} from '@babel/types'
import {
TUONO_MAIN_PACKAGE,
TUONO_DYNAMIC_FN_ID,
TUONO_LAZY_FN_ID,
} from './constants'
import { isTuonoDynamicFnImported } from './utils'
/**
* [SERVER build]
* This plugin just removes the `dynamic` imported function from any tuono import
+7 -4
View File
@@ -1,10 +1,13 @@
import { TUONO_MAIN_PACKAGE, TUONO_DYNAMIC_FN_ID } from './constants'
import type * as t from '@babel/types'
import type {
Identifier,
ImportDeclaration,
ImportSpecifier,
} from '@babel/types'
import type { Identifier, ImportDeclaration } from '@babel/types'
import { TUONO_MAIN_PACKAGE, TUONO_DYNAMIC_FN_ID } from './constants'
export const isTuonoDynamicFnImported = (
path: babel.NodePath<t.ImportSpecifier>,
path: babel.NodePath<ImportSpecifier>,
): boolean => {
if ((path.node.imported as Identifier).name !== TUONO_DYNAMIC_FN_ID) {
return false

Some files were not shown because too many files have changed in this diff Show More