Compare commits

..

17 Commits

Author SHA1 Message Date
Valerio Ageno d1b12173d5 feat: update version to v0.16.7 2024-12-23 18:32:15 +01:00
Marco Pasqualetti f3bf13da59 refactor(packages/*): add tuono- prefix to all relevant folders (#255) 2024-12-23 18:28:06 +01:00
Marco Pasqualetti f236b75f55 refactor(packages/*): use @vitejs/plugin-react-swc and make all package dependencies explicit (#254) 2024-12-23 18:20:29 +01:00
Marco Pasqualetti 2434231f9b chore: add eslint-plugin-react (#251) 2024-12-23 11:43:04 +01:00
Valerio Ageno b8b39e69a1 Add message event polyfill (#250) 2024-12-22 21:35:08 +01:00
Jovan Milosevic f8158dfd8e chore(examples): Remove tsconfig.node.js file from examples (#245) 2024-12-22 20:12:19 +01:00
Valerio Ageno f1ab7df5ce feat: udpdate version to v0.16.6 2024-12-22 18:06:40 +01:00
Marco Pasqualetti 986de49052 refactor: add eslint-plugin-react-hooks (#248) 2024-12-22 18:03:03 +01:00
Marco Pasqualetti d09ca5548a chore: add .editorconfig (#249) 2024-12-22 17:52:03 +01:00
Marco Pasqualetti 77e22c40b6 refactor: enable typescript-eslint strict rules (#243) 2024-12-22 12:08:21 +01:00
Marco Pasqualetti de74647c43 test(packages/tuono): messageChannel - use vi.fn (#241) 2024-12-22 09:30:18 +01:00
Mustafa Zaki Assagaf 498601c21a feat: add MessageChannel polyfill to support react 19 (#238) 2024-12-21 16:55:04 +01:00
Marco Pasqualetti e99f06ddf5 chore(bug_report): make system-info fields required and add CLI command to retrieve them (#239) 2024-12-20 07:34:55 +01:00
Marco Pasqualetti f06af59168 chore: add CODEOWNERS (#237) 2024-12-19 08:22:16 +01:00
Valerio Ageno d50e3a7fc0 feat: update version to v0.16.5 2024-12-18 22:10:05 +01:00
Valerio Ageno 3e4e7ff1ab feat: update rendering from renderToString to renderToReadableStream (#233)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2024-12-18 21:58:19 +01:00
Marco Pasqualetti 21ff3c8655 docs: update tuono (#236) 2024-12-18 21:53:24 +01:00
121 changed files with 1252 additions and 892 deletions
+7
View File
@@ -0,0 +1,7 @@
[*]
charset = utf-8
insert_final_newline = true
end_of_line = lf
indent_style = space
indent_size = 2
max_line_length = 80
+14 -45
View File
@@ -32,55 +32,24 @@ body:
description: If applicable, add screenshots to help explain your problem.
validations:
required: false
- type: input
id: tuono-version
- type: textarea
id: system-info
attributes:
label: Tuono version
placeholder: '[e.g. 0.4.0]'
label: System Info
description: Output of `npx envinfo --system --npmPackages 'tuono' --binaries --browsers`
render: shell
placeholder: System, Binaries, Browsers
validations:
required: false
- type: input
id: os-version
required: true
- type: textarea
id: system-info-rust
attributes:
label: OS
placeholder: '[e.g. MacOS, Windows]'
label: System info (Rust)
description: Output of `rustc --version && cargo --version && tuono --version`
render: shell
placeholder: 'rustc, cargo and tuono binary version'
validations:
required: false
- type: input
id: browser-version
attributes:
label: Browser
placeholder: '[e.g. chrome, safari]'
validations:
required: false
- type: input
id: node-version
attributes:
label: Node version
placeholder: '[e.g. 20.0.0]'
validations:
required: false
- type: input
id: rust-version
attributes:
label: Rust version
placeholder: '[e.g. 1.79.0]'
validations:
required: false
- type: input
id: create-version
attributes:
label: Crate version
placeholder: '[e.g. 1.78.0]'
validations:
required: false
- type: input
id: node-package-manager-version
attributes:
label: Node Package Manger version
placeholder: '[e.g. pnpm: 9.5.0] '
validations:
required: false
required: true
- type: textarea
id: additional-context
attributes:
+1 -1
View File
@@ -37,7 +37,7 @@ jobs:
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install tuono
run: cargo install tuono@0.15.0
run: cargo install tuono@0.16.4
- name: Build project
working-directory: ./apps/documentation
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install tuono
run: cargo install tuono@0.16.0
run: cargo install tuono@0.16.4
- name: Build project
working-directory: ./apps/documentation
+2
View File
@@ -8,6 +8,8 @@ dist/
examples/*/pnpm-lock.yaml
vite.config.ts.timestamp*
## Rust related ignores
# Generated by Cargo
+1 -1
View File
@@ -3,4 +3,4 @@ pnpm-lock.yaml
dist
.tuono
packages/lazy-fn-vite-plugin/tests/sources/*
packages/tuono-lazy-fn-vite-plugin/tests/sources/*
+9
View File
@@ -0,0 +1,9 @@
* @Valerioageno @marcalexiei
# Rust
/crates/ @Valerioageno
/Cargo.toml @Valerioageno
# Misc
/.github/ @marcalexiei
+1 -1
View File
@@ -8,7 +8,7 @@ name = "tuono"
path = ".tuono/main.rs"
[dependencies]
tuono_lib = "0.16.2"
tuono_lib = "0.16.4"
glob = "0.3.1"
time = { version = "0.3", features = ["macros"] }
serde = { version = "1.0.202", features = ["derive"] }
+1 -1
View File
@@ -19,7 +19,7 @@
"clsx": "2.1.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"tuono": "npm:tuono@0.16.2"
"tuono": "npm:tuono@0.16.4"
},
"devDependencies": {
"@mdx-js/rollup": "3.1.0",
@@ -16,6 +16,12 @@ export default function MdxTitle(props: TitleProps): JSX.Element {
)
}
export const h =
(order: 1 | 2 | 3 | 4 | 5 | 6) =>
(props: TitleProps): JSX.Element => <MdxTitle order={order} {...props} />
export const h = (
order: 1 | 2 | 3 | 4 | 5 | 6,
): React.ElementType<TitleProps> => {
function render(props: TitleProps): JSX.Element {
return <MdxTitle order={order} {...props} />
}
render.displayName = 'H'
return render
}
@@ -50,26 +50,30 @@ function SidebarElements({ close }: SidebarProps): JSX.Element {
<AppShell.Section component={ScrollArea}>
{sidebarElements.map((el, i) => {
if (el.type === 'divider') {
return <Divider my="md" mx={10} />
return <Divider key={`${el.type}-${i}`} my="md" mx={10} />
}
if (el.type === 'title') {
return (
<Text
key={`${el.type}-${i}`}
size="xs"
fw={700}
fz={12}
pl={12}
py={5}
children={el.label}
/>
>
{el.label}
</Text>
)
}
if (el.children?.length) {
return (
<SidebarLink
key={`${el.type}-${i}`}
href={el.href}
label={el.label}
key={i}
leftSection={el.leftIcon}
>
{el.children.map((child, index) => (
@@ -83,12 +87,13 @@ function SidebarElements({ close }: SidebarProps): JSX.Element {
</SidebarLink>
)
}
return (
<SidebarLink
key={`${el.type}-${i}`}
href={el.href}
label={el.label}
onClick={close}
key={i}
/>
)
})}
@@ -64,7 +64,9 @@ export function TableOfContents({
),
)
window.addEventListener('scroll', handleScroll)
return (): void => window.removeEventListener('scroll', handleScroll)
return (): void => {
window.removeEventListener('scroll', handleScroll)
}
}, [router.pathname])
if (filteredHeadings.length === 0) {
@@ -17,9 +17,9 @@ export default function ThemeBtn(): JSX.Element {
return (
<ActionIcon
onClick={() =>
onClick={() => {
setColorScheme(computedColorScheme === 'light' ? 'dark' : 'light')
}
}}
variant="default"
size="lg"
aria-label="Toggle color scheme"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tuono"
version = "0.16.4"
version = "0.16.7"
edition = "2021"
authors = ["V. Ageno <valerioageno@yahoo.it>"]
description = "Superfast React fullstack framework"
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tuono_lib"
version = "0.16.4"
version = "0.16.7"
edition = "2021"
authors = ["V. Ageno <valerioageno@yahoo.it>"]
description = "Superfast React fullstack framework"
@@ -32,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.16.4"}
tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.16.7"}
# Match the same version used by axum
tokio-tungstenite = "0.24.0"
futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] }
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tuono_lib_macros"
version = "0.16.4"
version = "0.16.7"
edition = "2021"
description = "Superfast React fullstack framework"
homepage = "https://tuono.dev"
+45 -14
View File
@@ -2,6 +2,9 @@ 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'
import eslintPluginReact from 'eslint-plugin-react'
// @ts-expect-error no types are available for this plugin
import eslintPluginReactHooks from 'eslint-plugin-react-hooks'
export default tseslint.config(
{
@@ -13,8 +16,8 @@ export default tseslint.config(
// #endregion shared
// #region package-specific
'packages/fs-router-vite-plugin/tests/generator/**',
'packages/lazy-fn-vite-plugin/tests/sources/**',
'packages/tuono-fs-router-vite-plugin/tests/generator/**',
'packages/tuono-lazy-fn-vite-plugin/tests/sources/**',
'packages/tuono/bin/**',
// #endregion package-specific
@@ -25,10 +28,34 @@ export default tseslint.config(
reportUnusedDisableDirectives: 'error',
},
},
eslint.configs.recommended,
/* eslint-disable @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access */
eslintPluginImport.flatConfigs.recommended,
eslintPluginImport.flatConfigs.typescript,
tseslint.configs.recommendedTypeChecked,
/* eslint-enable @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access */
// eslint-disable-next-line import/no-named-as-default-member
tseslint.configs.strictTypeChecked,
// @ts-expect-error flat is optional but always defined on runtime
eslintPluginReact.configs.flat.recommended,
// @ts-expect-error flat is optional but always defined on runtime
eslintPluginReact.configs.flat['jsx-runtime'],
{
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
plugins: {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
'react-hooks': eslintPluginReactHooks,
},
rules: {
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error',
},
},
{
languageOptions: {
parserOptions: {
@@ -50,16 +77,13 @@ export default tseslint.config(
rules: {
// #region @typescript-eslint
'@typescript-eslint/array-type': ['error', { default: 'generic' }],
'@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/explicit-function-return-type': 'error',
'@typescript-eslint/method-signature-style': ['error', 'property'],
'@typescript-eslint/naming-convention': [
'error',
@@ -74,19 +98,24 @@ export default tseslint.config(
},
},
],
'@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 },
],
'@typescript-eslint/restrict-template-expressions': [
'error',
{
allowAny: false,
allowBoolean: false,
allowNever: false,
allowNullish: false,
allowNumber: true,
allowRegExp: false,
},
],
// #endregion @typescript-eslint
// #region import
@@ -127,8 +156,10 @@ export default tseslint.config(
'no-shadow': 'error',
'no-undef': 'off',
'sort-imports': 'off',
// #endregion misc
},
},
{
files: ['apps/documentation/**'],
settings: {
+1 -2
View File
@@ -20,6 +20,5 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src", "tuono.config.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
"include": ["src", "tuono.config.ts"]
}
-11
View File
@@ -1,11 +0,0 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strict": true
},
"include": ["vite.config.ts"]
}
+1 -2
View File
@@ -23,6 +23,5 @@
"@/*": ["./src/*"]
}
},
"include": ["src", "tuono.config.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
"include": ["src", "tuono.config.ts"]
}
@@ -1,11 +0,0 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strict": true
},
"include": ["vite.config.ts"]
}
+1 -2
View File
@@ -20,6 +20,5 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src", "tuono.config.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
"include": ["src", "tuono.config.ts"]
}
-11
View File
@@ -1,11 +0,0 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strict": true
},
"include": ["vite.config.ts"]
}
+9 -14
View File
@@ -24,20 +24,15 @@
"license": "MIT",
"devDependencies": {
"@eslint/js": "9.15.0",
"@tanstack/config": "^0.7.0",
"@types/node": "^22.0.0",
"@types/react": "^18.3.13",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^9.15.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^5.0.0",
"prettier": "^3.2.4",
"turbo": "^2.2.3",
"@types/node": "22.10.0",
"eslint": "9.15.0",
"eslint-import-resolver-typescript": "3.6.3",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-react": "7.37.2",
"eslint-plugin-react-hooks": "5.1.0",
"prettier": "3.4.1",
"turbo": "2.2.3",
"typescript": "5.6.3",
"typescript-eslint": "8.15.0",
"vite": "^5.2.11",
"vitest": "^2.0.0"
"typescript-eslint": "8.15.0"
}
}
@@ -1,6 +1,6 @@
{
"name": "tuono-fs-router-vite-plugin",
"version": "0.16.4",
"version": "0.16.7",
"description": "Plugin for the tuono's file system router. Tuono is the react/rust fullstack framework",
"homepage": "https://tuono.dev",
"scripts": {
@@ -16,7 +16,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/tuono-labs/tuono.git",
"directory": "packages/fs-router-vite-plugin"
"directory": "packages/tuono-fs-router-vite-plugin"
},
"keywords": [],
"author": "Valerio Ageno",
@@ -50,7 +50,7 @@
"vite": "^5.2.11"
},
"devDependencies": {
"@tanstack/config": "^0.7.11",
"@tanstack/config": "0.7.13",
"@types/babel__core": "^7.20.5",
"vitest": "^2.0.0"
}
@@ -6,13 +6,13 @@ const routes = [
{
filePath: 'posts/my-post.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/my-post.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/my-post.tsx',
routePath: '/posts/my-post',
variableName: 'PostsMyPost',
parent: {
filePath: 'posts/__root.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/__root.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/__root.tsx',
routePath: '/posts/__root',
variableName: 'Postsroot',
path: '/posts/__root',
@@ -25,13 +25,13 @@ const routes = [
{
filePath: 'posts/index.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/index.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/index.tsx',
routePath: '/posts/',
variableName: 'PostsIndex',
parent: {
filePath: 'posts/__root.tsx',
fullPath:
'/home/valerio/Documents/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/__root.tsx',
'/home/valerio/Documents/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/__root.tsx',
routePath: '/posts/__root',
variableName: 'Postsroot',
path: '/posts/__root',
@@ -44,13 +44,13 @@ const routes = [
{
filePath: 'posts/[post].tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/index.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/index.tsx',
routePath: '/posts/',
variableName: 'PostspostIndex',
parent: {
filePath: 'posts/__root.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/__root.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/__root.tsx',
routePath: '/posts/__root',
variableName: 'Postsroot',
path: '/posts/__root',
@@ -3,7 +3,7 @@ import type { RouteNode } from './types'
export function buildRouteConfig(nodes: Array<RouteNode>, depth = 1): string {
const children = nodes.map((node) => {
const route = `${node.variableName}Route`
const route = `${node.variableName as string}Route`
if (node.children?.length) {
const childConfigs = buildRouteConfig(node.children, depth + 1)
@@ -157,14 +157,14 @@ export async function routeGenerator(config = defaultConfig): Promise<void> {
(d): number => (d.routePath.includes(`/${ROOT_PATH_ID}`) ? -1 : 1),
(d): number => d.routePath.split('/').length,
(d): number => (d.routePath.endsWith("index'") ? -1 : 1),
(d): any => d,
(d): RouteNode => d,
])
const imports = [
...sortedRouteNodes.map((node) => {
const extension = node.filePath.endsWith('mdx') ? '.mdx' : ''
return `const ${
node.variableName
node.variableName as string
}Import = dynamic(() => import('./${replaceBackslash(
removeExt(
path.relative(
@@ -181,18 +181,21 @@ export async function routeGenerator(config = defaultConfig): Promise<void> {
...sortedRouteNodes.map((node) => {
const isRoot = node.routePath.endsWith(ROOT_PATH_ID)
const rootDeclaration = isRoot ? ', isRoot: true' : ''
const variableName = node.variableName as string
return `const ${node.variableName} = createRoute({ component: ${node.variableName}Import${rootDeclaration} })`
return `const ${variableName} = createRoute({ component: ${variableName}Import${rootDeclaration} })`
}),
].join('\n')
const createRouteUpdates = [
sortedRouteNodes
.map((node) => {
const variableName = node.variableName as string
const cleanedPath = node.cleanedPath as string
return [
`const ${node.variableName}Route = ${node.variableName}.update({
`const ${variableName}Route = ${variableName}.update({
${[
!node.path?.endsWith(ROOT_PATH_ID) && `path: '${node.cleanedPath}'`,
!node.path?.endsWith(ROOT_PATH_ID) && `path: '${cleanedPath}'`,
`getParentRoute: () => ${node.parent?.variableName ?? 'root'}Route`,
rustHandlersNodes.includes(node.path || '')
? 'hasHandler: true'
@@ -236,7 +239,7 @@ export async function routeGenerator(config = defaultConfig): Promise<void> {
const routeTreeContent = await fsp
.readFile(path.resolve(config.generatedRouteTree), 'utf-8')
.catch((e) => {
.catch((e: unknown) => {
const err = e as Error & { code?: string }
if (err.code === 'ENOENT') {
return undefined
@@ -6,7 +6,7 @@ const routes = [
{
filePath: 'posts/[post].tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root/routes/posts/[post].tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root/routes/posts/[post].tsx',
routePath: '/posts/[post]',
variableName: 'Postspost',
path: '/posts/[post]',
@@ -15,7 +15,7 @@ const routes = [
{
filePath: 'posts/__root.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root/routes/posts/__root.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root/routes/posts/__root.tsx',
routePath: '/posts/__root',
variableName: 'Postsroot',
path: '/posts/__root',
@@ -24,7 +24,7 @@ const routes = [
{
filePath: 'index.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root/routes/index.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root/routes/index.tsx',
routePath: '/',
variableName: 'Index',
path: '/',
@@ -33,7 +33,7 @@ const routes = [
{
filePath: 'posts/my-post.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root/routes/posts/my-post.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root/routes/posts/my-post.tsx',
routePath: '/posts/my-post',
variableName: 'PostsMyPost',
path: '/posts/my-post',
@@ -44,7 +44,7 @@ const routes = [
const parent = {
filePath: 'posts/__root.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root/routes/posts/__root.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root/routes/posts/__root.tsx',
routePath: '/posts/__root',
variableName: 'Postsroot',
path: '/posts/__root',
@@ -54,7 +54,7 @@ const parent = {
const myPost = {
filePath: 'posts/my-post.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root/routes/posts/my-post.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root/routes/posts/my-post.tsx',
routePath: '/posts/my-post',
variableName: 'PostsMyPost',
path: '/posts/my-post',
@@ -64,7 +64,7 @@ const myPost = {
const dynamicRoute = {
filePath: 'posts/[post].tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root/routes/posts/[post].tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root/routes/posts/[post].tsx',
routePath: '/posts/[post]',
variableName: 'Postspost',
path: '/posts/[post]',
@@ -6,7 +6,7 @@ const routes = [
{
filePath: 'index.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/index.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/index.tsx',
routePath: '/',
variableName: 'Index',
path: '/',
@@ -15,7 +15,7 @@ const routes = [
{
filePath: 'about.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/about.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/about.tsx',
routePath: '/about',
variableName: 'About',
path: '/about',
@@ -24,35 +24,35 @@ const routes = [
{
filePath: '__root.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/__root.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/__root.tsx',
routePath: '/__root',
variableName: 'root',
},
{
filePath: 'posts/[post].tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/[post].tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/[post].tsx',
routePath: '/posts/[post]',
variableName: 'Postspost',
},
{
filePath: 'posts/my-post.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/my-post.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/my-post.tsx',
routePath: '/posts/my-post',
variableName: 'PostsMyPost',
},
{
filePath: 'posts/index.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/index.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/index.tsx',
routePath: '/posts/',
variableName: 'PostsIndex',
},
{
filePath: 'posts/__root.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/__root.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/__root.tsx',
routePath: '/posts/__root',
variableName: 'Postsroot',
},
@@ -62,7 +62,7 @@ const expectedSorting = [
{
filePath: 'index.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/index.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/index.tsx',
routePath: '/',
variableName: 'Index',
path: '/',
@@ -71,7 +71,7 @@ const expectedSorting = [
{
filePath: 'about.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/about.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/about.tsx',
routePath: '/about',
variableName: 'About',
path: '/about',
@@ -80,28 +80,28 @@ const expectedSorting = [
{
filePath: 'posts/__root.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/__root.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/__root.tsx',
routePath: '/posts/__root',
variableName: 'Postsroot',
},
{
filePath: 'posts/my-post.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/my-post.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/my-post.tsx',
routePath: '/posts/my-post',
variableName: 'PostsMyPost',
},
{
filePath: 'posts/index.tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/index.tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/index.tsx',
routePath: '/posts/',
variableName: 'PostsIndex',
},
{
filePath: 'posts/[post].tsx',
fullPath:
'/tuono/packages/fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/[post].tsx',
'/tuono/packages/tuono-fs-router-vite-plugin/tests/generator/multi-level-root-dynamic/routes/posts/[post].tsx',
routePath: '/posts/[post]',
variableName: 'Postspost',
},
@@ -1,6 +1,6 @@
{
"name": "tuono-lazy-fn-vite-plugin",
"version": "0.16.4",
"version": "0.16.7",
"description": "Plugin for the tuono's lazy fn. Tuono is the react/rust fullstack framework",
"homepage": "https://tuono.dev",
"scripts": {
@@ -16,7 +16,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/tuono-labs/tuono.git",
"directory": "packages/lazy-fn-vite-plugin"
"directory": "packages/tuono-lazy-fn-vite-plugin"
},
"keywords": [],
"author": "Valerio Ageno",
@@ -49,7 +49,7 @@
"vite": "^5.2.11"
},
"devDependencies": {
"@tanstack/config": "^0.7.11",
"@tanstack/config": "0.7.13",
"@types/babel__core": "^7.20.5",
"prettier": "^3.2.4",
"vitest": "^2.0.0"
@@ -1,6 +1,6 @@
{
"name": "tuono-router",
"version": "0.16.4",
"version": "0.16.7",
"description": "React routing component for the framework tuono. Tuono is the react/rust fullstack framework",
"homepage": "https://tuono.dev",
"scripts": {
@@ -16,7 +16,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/tuono-labs/tuono.git",
"directory": "packages/router"
"directory": "packages/tuono-router"
},
"keywords": [],
"author": "Valerio Ageno",
@@ -44,8 +44,7 @@
"./package.json": "./package.json"
},
"peerDependencies": {
"react": ">=16.3.0",
"react-dom": ">=16.3.0"
"react": ">=16.3.0"
},
"dependencies": {
"react-intersection-observer": "^9.13.0",
@@ -53,11 +52,13 @@
"zustand": "4.4.7"
},
"devDependencies": {
"@tanstack/config": "^0.7.11",
"@tanstack/config": "0.7.13",
"@testing-library/jest-dom": "^6.6.0",
"@testing-library/react": "^16.0.0",
"@types/react": "18.3.13",
"@vitejs/plugin-react-swc": "3.7.2",
"react": "18.3.1",
"jsdom": "^25.0.0",
"prettier": "^3.2.4",
"vitest": "^2.0.0"
}
}
@@ -1,27 +1,37 @@
import * as React from 'react'
import type { AnchorHTMLAttributes } from 'react'
import type * as React from 'react'
import { useInView } from 'react-intersection-observer'
import { useRouter } from '../hooks/useRouter'
import useRoute from '../hooks/useRoute'
interface TuonoLinkProps {
interface TuonoLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
/**
* If "true" the route gets loaded when the link enters the viewport. Default "true"
* If "true" the route gets loaded when the link enters the viewport.
* @default true
*/
preload?: boolean
/**
* If "false" the scroll offset will be kept across page navigation. Default "true"
* If "false" the scroll offset will be kept across page navigation.
* @default true
*/
scroll?: boolean
}
export default function Link(
componentProps: AnchorHTMLAttributes<HTMLAnchorElement> & TuonoLinkProps,
componentProps: TuonoLinkProps,
): React.JSX.Element {
const { preload = true, scroll = true, ...props } = componentProps
const {
preload = true,
scroll = true,
children,
href,
onClick,
...rest
} = componentProps
const router = useRouter()
const route = useRoute(props.href)
const route = useRoute(href)
const { ref } = useInView({
onChange(inView) {
if (inView && preload) route?.component.preload()
@@ -29,21 +39,23 @@ export default function Link(
triggerOnce: true,
})
const handleTransition: React.MouseEventHandler<HTMLAnchorElement> = (e) => {
e.preventDefault()
props.onClick?.(e)
const handleTransition: React.MouseEventHandler<HTMLAnchorElement> = (
event,
) => {
event.preventDefault()
onClick?.(event)
if (props.href?.startsWith('#')) {
window.location.hash = props.href
if (href?.startsWith('#')) {
window.location.hash = href
return
}
router.push(props.href || '', { scroll })
router.push(href || '', { scroll })
}
return (
<a {...props} ref={ref} onClick={handleTransition}>
{props.children}
<a {...rest} href={href} ref={ref} onClick={handleTransition}>
{children}
</a>
)
}
@@ -1,4 +1,4 @@
import * as React from 'react'
import type * as React from 'react'
import { useRouterStore } from '../hooks/useRouterStore'
import useRoute from '../hooks/useRoute'
@@ -1,4 +1,4 @@
import * as React from 'react'
import type * as React from 'react'
import { useInternalRouter } from '../hooks/useInternalRouter'
@@ -1,4 +1,4 @@
import * as React from 'react'
import type * as React from 'react'
import { afterEach, describe, expect, test, vi } from 'vitest'
import { cleanup, render, screen } from '@testing-library/react'
@@ -42,7 +42,7 @@ describe('Test RouteMatch component', () => {
test('It should correctly render nested routes', () => {
vi.mock('../hooks/useServerSideProps.tsx', () => ({
useServerSideProps: (): { data: any; isLoading: boolean } => {
useServerSideProps: (): { data: unknown; isLoading: boolean } => {
return {
data: undefined,
isLoading: false,
@@ -20,6 +20,7 @@ export const RouteMatch = ({
}: RouteMatchProps): React.JSX.Element => {
const { data, isLoading } = useServerSideProps(route, serverSideProps)
// eslint-disable-next-line react-hooks/exhaustive-deps
const routes = React.useMemo(() => loadParentComponents(route), [route.id])
return (
@@ -29,12 +30,6 @@ export const RouteMatch = ({
)
}
interface ParentProps<TData = unknown> {
children: React.ReactNode
data: TData
isLoading: boolean
}
interface TraverseRootComponentsProps<TData = unknown> {
routes: Array<Route>
data: TData
@@ -58,13 +53,7 @@ const TraverseRootComponents = React.memo(
children,
}: TraverseRootComponentsProps): React.JSX.Element => {
if (routes.length > index) {
const Parent = React.useMemo(
() =>
routes[index]?.component as unknown as (
props: ParentProps,
) => React.JSX.Element,
[],
)
const Parent = (routes[index] as Route).component
return (
<Parent data={data} isLoading={isLoading}>
@@ -83,6 +72,7 @@ const TraverseRootComponents = React.memo(
return <>{children}</>
},
)
TraverseRootComponents.displayName = 'TraverseRootComponents'
const loadParentComponents = (
route: Route,
@@ -2,7 +2,7 @@ import React from 'react'
import type { ReactNode, JSX } from 'react'
import { useListenBrowserUrlUpdates } from '../hooks/useListenBrowserUrlUpdates'
import { initRouterStore } from '../hooks/useRouterStore'
import { useInitRouterStore } from '../hooks/useRouterStore'
import type { ServerProps } from '../types'
import type { Router } from '../router'
@@ -39,7 +39,7 @@ export function RouterProvider({
router,
serverProps,
}: RouterProviderProps): JSX.Element {
initRouterStore(serverProps)
useInitRouterStore(serverProps)
useListenBrowserUrlUpdates()
@@ -1,9 +1,9 @@
import * as React from 'react'
import type { ReactElement, ComponentType } from 'react'
import type { ReactElement } from 'react'
import type { RouteComponent } from './types'
type ImportFn = () => Promise<{ default: React.ComponentType<any> }>
type ImportFn = () => Promise<{ default: RouteComponent }>
/**
* Helper function to lazy load any component.
@@ -44,7 +44,7 @@ export const dynamic = (importFn: ImportFn): React.JSX.Element => {
export const __tuono__internal__lazyLoadComponent = (
factory: ImportFn,
): RouteComponent => {
let LoadedComponent: ComponentType<any> | undefined
let LoadedComponent: RouteComponent | undefined
const LazyComponent = React.lazy(factory) as unknown as RouteComponent
const loadComponent = (): Promise<void> =>
@@ -52,7 +52,9 @@ export const __tuono__internal__lazyLoadComponent = (
LoadedComponent = module.default
})
const Component = (props: any): ReactElement =>
const Component = (
props: React.ComponentProps<RouteComponent>,
): ReactElement =>
React.createElement(LoadedComponent || LazyComponent, props)
Component.preload = loadComponent
@@ -9,23 +9,26 @@ import { useRouterStore } from './useRouterStore'
export const useListenBrowserUrlUpdates = (): void => {
const updateLocation = useRouterStore((st) => st.updateLocation)
const updateLocationOnPopStateChange = ({ target }: PopStateEvent): void => {
const { pathname, hash, href, search } = (target as typeof window).location
updateLocation({
pathname,
hash,
href,
searchStr: search,
search: Object.fromEntries(new URLSearchParams(search)),
})
}
useEffect(() => {
const updateLocationOnPopStateChange = ({
target,
}: PopStateEvent): void => {
const { location } = target as typeof window
const { pathname, hash, href, search } = location
updateLocation({
pathname,
hash,
href,
searchStr: search,
search: Object.fromEntries(new URLSearchParams(search)),
})
}
window.addEventListener('popstate', updateLocationOnPopStateChange)
return (): void => {
window.removeEventListener('popstate', updateLocationOnPopStateChange)
}
}, [])
}, [updateLocation])
}
@@ -8,7 +8,7 @@ describe('Test useRoute fn', () => {
test('match routes by ids', () => {
vi.mock('./useInternalRouter.tsx', () => ({
useInternalRouter: (): { routesById: Record<string, any> } => {
useInternalRouter: (): { routesById: Record<string, { id: string }> } => {
return {
routesById: {
'/': { id: '/' },
@@ -25,12 +25,12 @@ export function sanitizePathname(pathname: string): string {
* Optimizations should occour on both
*/
export default function useRoute(pathname?: string): Route | undefined {
const { routesById } = useInternalRouter()
if (!pathname) return
pathname = sanitizePathname(pathname)
const { routesById } = useInternalRouter()
if (routesById[pathname]) return routesById[pathname]
const dynamicRoutes = Object.keys(routesById).filter((route) =>
@@ -16,7 +16,7 @@ interface UseRouterHook {
/**
* This object contains all the query params of the current route
*/
query: Record<string, any>
query: Record<string, unknown>
/**
* Returns the current pathname
@@ -23,7 +23,7 @@ interface RouterState {
updateLocation: (loc: ParsedLocation) => void
}
export const initRouterStore = (props?: ServerProps): void => {
export const useInitRouterStore = (props?: ServerProps): void => {
const updateLocation = useRouterStore((st) => st.updateLocation)
// Init the store in the server in order to correctly
@@ -49,7 +49,7 @@ export const initRouterStore = (props?: ServerProps): void => {
searchStr: search,
search: Object.fromEntries(new URLSearchParams(search)),
})
}, [])
}, [updateLocation])
}
export const useRouterStore = create<RouterState>()((set) => ({
@@ -67,5 +67,7 @@ export const useRouterStore = create<RouterState>()((set) => ({
pendingMatches: [],
cachedMatches: [],
statusCode: 200,
updateLocation: (location: ParsedLocation): void => set({ location }),
updateLocation: (location: ParsedLocation): void => {
set({ location })
},
}))

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