Compare commits

...

208 Commits

Author SHA1 Message Date
Valerio Ageno bf77ed1794 chore: update version to v0.17.10 (#615) 2025-02-28 17:41:14 +01:00
Valerio Ageno e34c10bc34 fix: build script for creation of parent directories (#614) 2025-02-28 17:41:01 +01:00
Marco Pasqualetti 310579cf76 fix(examples/tuono-tutorial): use pokemon name as key instead of index (#613) 2025-02-28 14:17:26 +01:00
pveierland 1ba94238ed feat: add origin config option (#582)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2025-02-27 18:25:03 +01:00
Valerio Ageno 5f6bad637c chore: update version to v0.17.9 (#608) 2025-02-27 08:35:42 +01:00
Marco Pasqualetti 3063763667 feat(env): temporary revert frontend implementation (#609) 2025-02-27 08:35:06 +01:00
Valerio Ageno caf1a58a0b feat: update axum to v0.8.1 (#595) 2025-02-26 19:01:10 +01:00
Marco Pasqualetti 059f3d6476 chore(deps): update dependency prettier to v3.5.2 (#602) 2025-02-24 20:30:12 +01:00
renovate[bot] 303a177344 chore(deps): update dependency vitest to v3.0.7 (#601)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 20:21:16 +01:00
renovate[bot] d26918fc2f chore(deps): update dependency happy-dom to v17.1.4 (#598)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 20:15:27 +01:00
renovate[bot] 31f0a167e9 chore(deps): update dependency @types/node to v22.13.5 (#597)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 20:04:09 +01:00
Jovan Milosevic d71185c9cb feat(packages/tuono-router): add replace method to router and replace prop to Link (#596) 2025-02-24 19:53:59 +01:00
Valerio Ageno 798f8f1bf8 feat: allow custom route with .txt extension (#594) 2025-02-24 12:18:14 +01:00
Marco Pasqualetti 06612f07b0 refactor(examples): uniform tsconfig.json files (#591) 2025-02-23 15:36:04 +01:00
Marco Pasqualetti 067535c2f6 test(packages/tuono): create-json-config test are not clearing writeFileSpy mock (#593) 2025-02-23 15:31:56 +01:00
Jacob Marshall d679982949 feat(env): setup env prefix and typescript for client-side (#584) 2025-02-23 14:17:23 +01:00
Jacob Marshall de5ac53cc3 feat: more detailed errors for Failed to read tuono.config.ts (#579) 2025-02-23 12:15:12 +01:00
Robert Lawson ca4a7b3466 test: add full integration test for tuono new CLI command (#411)
Co-authored-by: Valerio Ageno <valerioageno@yahoo.it>
2025-02-23 11:06:06 +01:00
Marco Pasqualetti 0a4d1e2995 feat: update vite to v6 and vitest v3 (#576) 2025-02-22 21:06:38 +01:00
Jacob Marshall d6fc18e2f6 chore(examples/with-tailwind): update tailwind to v4.0.6 (#577) 2025-02-17 20:58:42 +01:00
Valerio Ageno 8e04abbe42 feat: setup default tracing subscriber (#564) 2025-02-17 20:49:51 +01:00
Marco Pasqualetti 2905cfae3d chore: add devtools to labeler.yml (#575) 2025-02-17 18:07:56 +01:00
Jacob Marshall b632cf43ba refactor: apply RustRover lint suggestions (#569) 2025-02-17 08:40:03 +01:00
Jacob Marshall 06bc3700b7 fix: use snake_case for handler names (#565) 2025-02-16 18:53:40 +01:00
Marco Pasqualetti d4648afe01 chore: correct missing types task reference (#572) 2025-02-16 16:15:59 +01:00
renovate[bot] 0653349c08 chore(deps): update dependency turbo to v2.4.2 (#496)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-16 15:49:29 +01:00
Marco Pasqualetti 12e0937174 chore: add internal vite config (#568) 2025-02-16 15:39:25 +01:00
Marco Pasqualetti d37b655add feat(crates/tuono): feat: add spinner to tuono build (#560) 2025-02-14 19:20:22 +01:00
Valerio Ageno e3b1c0e013 feat: create body extractor function (#561) 2025-02-14 17:39:37 +01:00
Valerio Ageno be77068c73 chore: update version to v0.17.8 (#559) 2025-02-12 18:34:06 +01:00
Marco Pasqualetti 907c92bedc chore: remove documentation (#556) 2025-02-12 17:01:35 +01:00
Jovan Milosevic 6794ab20d3 feat: Add e2e setup and tests (#535) 2025-02-12 08:57:18 +01:00
Valerio Ageno e00dad9200 feat: polyfill URLSearchParams on the server side (#555) 2025-02-12 08:35:36 +01:00
Valerio Ageno 9aa6aedee0 feat: add spinner to 'tuono dev' (#550) 2025-02-11 17:21:28 +01:00
renovate[bot] 0114867fe6 chore(deps): update dependency typescript-eslint to v8.24.0 (#551)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 20:29:29 +01:00
renovate[bot] 4f58d42582 chore(deps): update devdependencies (eslint) to v9.20.0 (#546)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 17:44:23 +01:00
Marco Pasqualetti 40add60fd8 chore(pnpm): dedupe dependencies (#547) 2025-02-10 17:31:42 +01:00
renovate[bot] 9a4bad95ed chore(deps): update dependency prettier to v3.5.0 (#545)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 09:15:42 +01:00
renovate[bot] 3e28aeea27 fix(deps): update dependency posthog-js to v1.215.7 (#543)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 08:56:18 +01:00
renovate[bot] 58e48147b8 chore(deps): update dependency @types/node to v22.13.1 (#541)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 08:36:19 +01:00
Marco Pasqualetti 68eda84053 test(packages/tuono-router): use screen instead of render matchers (#540) 2025-02-09 19:01:30 +01:00
Jacob Marshall 253f35dbbb fix: update link component to allow modifier+click to properly work, and write tests (#534) 2025-02-09 18:50:45 +01:00
Jacob Marshall 0f72506f95 feat: add css key to vite config (#518) 2025-02-08 16:01:26 +01:00
Marco Pasqualetti ebe1abe422 docs: fix broken links (#532) 2025-02-07 21:02:41 +01:00
Valerio Ageno f5a7480f69 docs: add API routes page (#528)
Co-authored-by: Marco Pasqualetti <24919330+marcalexiei@users.noreply.github.com>
2025-02-07 19:07:58 +01:00
Marco Pasqualetti c0139fddc8 docs(sitemap.xml): exclude 404 page (#530) 2025-02-07 19:00:43 +01:00
Valerio Ageno 9e08a949bb docs: update tuono version to v0.17.7 (#524) 2025-02-07 18:50:46 +01:00
Valerio Ageno d86c97979b chore: bump version to v0.17.7 (#523) 2025-02-07 18:35:14 +01:00
Valerio Ageno 4e266522b6 chore: update ssr_rs to v0.8.1 (#529) 2025-02-07 18:15:16 +01:00
Marco Pasqualetti a58a1a4da9 chore(deps): update dependency turbo to v2.4.0 (#525) 2025-02-07 12:07:15 +01:00
Valerio Ageno 22c1dd04c2 docs: enable posthog analytics (#519) 2025-02-06 20:58:33 +01:00
Valerio Ageno 74e34dbbf9 fix: with-tailwind template download (#522) 2025-02-06 20:55:30 +01:00
renovate[bot] 4f38de3dea chore(deps): update devdependencies (eslint) (#497)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-06 20:22:08 +01:00
Marco Pasqualetti 9d97f2d345 chore: refine issue templates (#508) 2025-02-06 16:21:13 +01:00
Valerio Ageno 938cf3d8c6 docs: add footer to doc (#512) 2025-02-06 15:03:39 +01:00
Valerio Ageno e1443274e3 docs: add privacy policy (#517) 2025-02-06 12:50:28 +01:00
Valerio Ageno b9f8c0312f docs: add mdx page (#514)
Co-authored-by: Marco Pasqualetti <24919330+marcalexiei@users.noreply.github.com>
2025-02-06 12:48:49 +01:00
Marco Pasqualetti 674a817131 docs: add /styles/css-modules page (#513) 2025-02-06 12:41:59 +01:00
Valerio Ageno 2cbe53f7b7 docs: fix typo (#516) 2025-02-06 12:04:31 +01:00
Valerio Ageno f3033ef0d2 docs: fix quote blocks (#515) 2025-02-06 10:33:53 +01:00
Valerio Ageno 0a0fb8c7f8 docs: add Request page (#502)
Co-authored-by: Marco Pasqualetti <24919330+marcalexiei@users.noreply.github.com>
2025-02-06 09:54:47 +01:00
Valerio Ageno f72a1a9e5c docs: improve readability (#510) 2025-02-05 18:33:09 +01:00
Valerio Ageno fb29a26246 docs: refine dynamic page hierarchy (#511) 2025-02-05 17:45:12 +01:00
Valerio Ageno 79d4aff758 docs: add TuonoScript component mention (#509)
Co-authored-by: Marco Pasqualetti <24919330+marcalexiei@users.noreply.github.com>
2025-02-05 16:44:43 +01:00
Jacob Marshall 7ea8414d6c docs: write page for dynamic function (#503) 2025-02-05 15:01:49 +01:00
Marco Pasqualetti 1a3c9a7fbe feat: refine package.json version ranges (#507) 2025-02-05 09:28:18 +01:00
renovate[bot] cecf2ba8cc chore(deps): update dependency vitest to v2.1.9 [security] (#505)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-05 06:42:15 +01:00
Valerio Ageno eaaf2513d7 docs: add SSG page (#498)
Co-authored-by: Marco Pasqualetti <24919330+marcalexiei@users.noreply.github.com>
2025-02-04 15:58:34 +01:00
Valerio Ageno fa4d435320 docs: add Response enum page (#500)
Co-authored-by: Marco Pasqualetti <24919330+marcalexiei@users.noreply.github.com>
2025-02-04 15:48:26 +01:00
Marco Pasqualetti 2aff72942c docs: add getting-started/setup-project (#488) 2025-02-04 09:18:03 +01:00
renovate[bot] 11666fc422 chore(deps): update dependency happy-dom to v16.8.1 (#494)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 20:01:27 +01:00
renovate[bot] aef456cbf2 chore(deps): update dependency @testing-library/react to v16.2.0 (#490)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 20:00:38 +01:00
Marco Pasqualetti 97b9d71caa docs: add custom 404 page (#486) 2025-02-03 19:57:14 +01:00
renovate[bot] 5fa38b9506 chore(deps): update dependency @types/node to v22.13.0 (#492)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 18:16:22 +01:00
Marco Pasqualetti 98e50da575 docs: remove "How is tuono different?" and refine “Installation” (#480) 2025-02-03 09:44:13 +01:00
Marco Pasqualetti c8cf0771d4 test(packages/tuono-router): refine unit tests (#478) 2025-02-02 17:45:16 +01:00
Valerio Ageno 1c6e78ad57 docs: add SSR page (#473) 2025-02-02 09:39:50 +01:00
Valerio Ageno 2b6f8c16ce docs: remove valerioageno email reference (#477) 2025-02-02 09:22:16 +01:00
Jacob Marshall 2403ef3d64 docs: indent ToC based on heading levels (#475) 2025-02-02 09:04:30 +01:00
renovate[bot] 003c0766cf chore(deps): update node.js to v22.13.1 (#445)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-31 18:00:08 +01:00
renovate[bot] 9d7e59d793 chore(deps): update dependency turbo to v2.3.4 (#430)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-31 17:51:22 +01:00
Valerio Ageno 43f03adedf fix: heading order (#471) 2025-01-30 21:20:34 +01:00
Valerio Ageno 211c6401e9 docs: refine UI (#466) 2025-01-30 21:04:15 +01:00
Jacob Marshall 01b90b327e docs: add headings containing only code to ToC (#467)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2025-01-30 08:48:07 +01:00
Jacob Marshall ad0b09c3df fix(docs): fix all line and link overflow issues (#426) 2025-01-29 18:42:53 +01:00
Marco Pasqualetti 362e1be58d ci: get documentation tuono version automatically (#462) 2025-01-29 17:32:35 +01:00
Marco Pasqualetti eb9039daa3 fix(crates/tuono_lib): print server url as valid URL to be clickable on CLI (#464) 2025-01-29 17:31:06 +01:00
Marco Pasqualetti ff7433f434 docs: use consistent snippet style across the codebase (#451)
Co-authored-by: wd <52734504+wdcodecn@users.noreply.github.com>
2025-01-29 08:33:55 +01:00
Marco Pasqualetti a4e207688a docs: update to tuono 0.17.6 (#461) 2025-01-29 07:20:05 +01:00
Valerio Ageno fdc87ed8f1 chore: bump version to v0.17.6 (#453) 2025-01-28 19:08:36 +01:00
Valerio Ageno 0ab9c297a5 test: integration test to tuono_lib (#425) 2025-01-28 19:08:18 +01:00
Marco Pasqualetti 04d6fcc1e1 feat(examples/tuono-tutorial): rewrite tuono-tutorial (#449) 2025-01-28 19:07:36 +01:00
Marco Pasqualetti bf18ac0ab0 docs: add notes about pull request and validating changes (#427) 2025-01-28 18:53:31 +01:00
Marco Pasqualetti bbe9605f6f docs(configuration): add documentation for server option (#446)
Co-authored-by: Valerio Ageno <51341197+Valerioageno@users.noreply.github.com>
2025-01-28 08:53:49 +01:00
Marco Pasqualetti bd34c194cb ci: add concurrency flag for CI workflows (#448) 2025-01-28 08:30:33 +01:00
Marco Pasqualetti c0aa707315 docs(tutorial): correct api fetching example (#447) 2025-01-28 08:17:32 +01:00
Jacob Marshall b6fa448202 fix: check if project contains tuono.config.ts before running dev/build (#420) 2025-01-27 20:16:12 +01:00
Marco Pasqualetti 2e141be0fc test(packages/tuono-router): replace jsdom with happy-dom (#444) 2025-01-27 20:00:28 +01:00
Marco Pasqualetti 93b5e3e68f chore(deps): update dependency @types/react to v19.0.8 (#442) 2025-01-27 19:42:29 +01:00
spacecodeur 39dac5065f ci: add docker image test workflow (#332) 2025-01-27 19:29:08 +01:00
Marco Pasqualetti a16a313f71 ci(pr-title-checker): test type is marked as invalid (#432) 2025-01-27 01:11:11 +01:00
Jacob Marshall 82560ddc20 fix(docs): content overflow (#423) 2025-01-26 19:27:33 +01:00
Jacob Marshall 83f3cae6f0 docs: table of contents (#394)
Co-authored-by: Valerio Ageno <valerioageno@yahoo.it>
2025-01-26 16:36:18 +01:00
Marco Pasqualetti 99df4a5575 docs: update tuono to 0.17.5 (#416) 2025-01-26 15:02:49 +01:00
Valerio Ageno d33c211a01 chore: bump version to v0.17.5 (#417) 2025-01-25 21:16:36 +01:00
Marco Pasqualetti 21ee89ada4 fix(packages/tuono): build error when config contains vite plugins (#415) 2025-01-25 18:12:57 +01:00
Marco Pasqualetti dcedb1c6c0 refactor: create TuonoContext (#413) 2025-01-25 15:56:10 +01:00
Jacob Marshall d5cc65983e feat(crates/tuono): add --head flag to download most recent template (#408)
Co-authored-by: Valerio Ageno <51341197+Valerioageno@users.noreply.github.com>
2025-01-25 13:42:29 +01:00
Valerio Ageno 3cb150b090 chore: bump version 0.17.4 (#409) 2025-01-23 18:57:14 +01:00
Valerio Ageno 305ae59086 feat: pass config from server to client (#407) 2025-01-23 18:43:17 +01:00
Robert Lawson 8fe8930def feat: Improve error handling (#398) 2025-01-22 20:41:59 +01:00
Valerio Ageno 8730bdbfb9 refactor: streamline readme (#403) 2025-01-22 19:07:30 +01:00
Valerio Ageno 3ee47e9481 feat: load config in tuono CLI (#399) 2025-01-22 18:01:29 +01:00
Marco Pasqualetti 4e649acd60 ci(docs): use tuono 0.17.3 in docs workflows (#404) 2025-01-22 16:07:21 +01:00
Valerio Ageno 4768d70a8a docs: remove "Components > Head" entry from sidebar (#402)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2025-01-22 13:55:19 +01:00
Marco Pasqualetti c7bff7ed09 fix(deps): update dependency vite to v5.4.12 [security] (#401) 2025-01-22 08:32:23 +01:00
Valerio Ageno 3035d2a645 feat: create tuono_internal crate (#395) 2025-01-21 19:01:37 +01:00
renovate[bot] 0750037385 fix(deps): update mantine monorepo to v7.16.1 (#388)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 09:49:18 +01:00
Marco Pasqualetti 8c4d61ee65 chore(deps): update dependency @types/react to v19.0.7 (#392)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 09:09:37 +01:00
Valerio Ageno 1a3ccb112e feat: add host and port support to TuonoConfig (#366)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2025-01-20 21:00:47 +01:00
Jacob Marshall 118172d27f refactor: add TuonoEntryPoint (#374)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2025-01-20 20:17:49 +01:00
renovate[bot] 69f1d85360 chore(deps): update dependency @types/node to v22.10.7 (#375)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 19:58:11 +01:00
Marco Pasqualetti f0d042c85b chore(packages/tuono): remove dynamic orphan files (#385) 2025-01-20 16:30:30 +01:00
Jacob Marshall 550830c232 chore: update documentation to tuono 0.17.x (#364) 2025-01-20 16:21:35 +01:00
Valerio Ageno 6be12b4e58 chore: bump version to v0.17.3 (#372) 2025-01-19 16:24:19 +01:00
Valerio Ageno 33aa37ed7d fix: create a component tree that matches between client and server to avoid useId output mismatch (#371)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2025-01-19 16:23:30 +01:00
Marco Pasqualetti 43910fd75d ci: add unique names on ci_ok jobs (#370) 2025-01-19 11:52:57 +01:00
LeNei 5efcc86a63 fix: propagate build error and check if js files exist (#297) 2025-01-19 09:35:11 +01:00
Valerio Ageno 17b61a5a41 chore: bump to v0.17.2 (#365) 2025-01-18 20:51:28 +01:00
Jacob Marshall 5dae5c31b7 feat: add a with-tailwind example (#359)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2025-01-18 20:44:45 +01:00
Marco Pasqualetti 96deea7f31 ci: redesign CI-related workflows to allow marking them as "Required” before merging a PR (#368) 2025-01-18 20:36:06 +01:00
Jacob Marshall 7632e6a464 fix: broken docs tables (#361) 2025-01-17 15:11:57 +01:00
Jacob Marshall 23e2a2e3ff docs(styles): add Tailwind CSS (#351)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2025-01-16 18:50:56 +01:00
Jacob Marshall b19512dffc docs: move from bullet points to tables for reference docs (#352)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2025-01-16 18:42:37 +01:00
Valerio Ageno 11c6fc820b chore: bump version to v0.17.1 (#357) 2025-01-16 12:34:59 +01:00
Valerio Ageno 1a8231851d feat: add support for client only lazy loading with dynamic (#331) 2025-01-16 12:34:06 +01:00
Valerio Ageno 1dfb83b1aa fix: revert "chore: bump version to v0.17.1" (#356) 2025-01-16 12:23:46 +01:00
Valerio Ageno 93cb1ae8ba chore: bump version to v0.17.1 (#354) 2025-01-15 19:24:34 +01:00
Jacob Marshall 631b168e05 docs: more cohesive documentation for linking and navigation, Link and useRouter (#335) 2025-01-15 19:17:35 +01:00
Marco Pasqualetti af8a43c93a docs: use PascalCase for file name (#350) 2025-01-15 16:59:36 +01:00
Valerio Ageno 875d12f514 docs(configuration): remove unclear doc (#349) 2025-01-15 13:02:48 +01:00
Jacob Marshall a9a2ef67ea fix(packages/tuono): config - correct vite.plugins type (#346) 2025-01-14 21:32:51 +01:00
Marco Pasqualetti 5caf3ed6e7 chore(renovate): fix schedule (#345) 2025-01-14 21:19:14 +01:00
Marco Pasqualetti 580ec704f2 chore: dedupe deps (#347) 2025-01-14 21:12:47 +01:00
Marco Pasqualetti a9918ae555 chore(renovate): change prConcurrentLimit to 5 (default 10) and schedule once per week (#340) 2025-01-14 20:43:25 +01:00
Marco Pasqualetti 4b3f4bfab8 docs(configuration): add types and reformat vite properties (#342) 2025-01-14 20:42:54 +01:00
Jacob Marshall fed3b1581b chore: remove RouterProvider from tuono exports (#337) 2025-01-14 16:43:16 +01:00
Jacob Marshall 9b2f0aab32 chore: refine useRouter return type and update docs (#339) 2025-01-14 16:42:27 +01:00
renovate[bot] 818a6bd2ff chore(deps): update dependency typescript to v5.7.3 (#309)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 09:43:27 +01:00
renovate[bot] 94a43347a1 chore(deps): update dependency turbo to v2.3.3 (#308)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 09:14:00 +01:00
renovate[bot] 16d793c588 chore(deps): update devdependencies (eslint) (#311)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 08:56:10 +01:00
renovate[bot] 0322e5bd8d chore(deps): update dependency postcss to v8.5.0 (#334)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 08:55:44 +01:00
renovate[bot] cfe76a9ab3 chore(deps): update dependency @types/node to v22.10.6 (#329)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 08:45:25 +01:00
renovate[bot] ff6014317b chore(deps): update pnpm to v9.15.4 (#333)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 08:42:06 +01:00
PRAMOD A JINGADE 64220f2e27 docs(configuration): fill page content (#307)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2025-01-14 06:48:57 +01:00
Marco Pasqualetti e75414abfc fix(deps): revert update dependency @babel/types to v7.26.5 (#328) 2025-01-13 20:31:34 +01:00
Jacob Marshall d6fae42dc3 docs: Add useRouter Documentation (#325) 2025-01-13 18:41:31 +01:00
renovate[bot] 0db1245a6b fix(deps): update rust crate ssr_rs to 0.8.0 (#324)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 18:26:50 +01:00
renovate[bot] 91c0a9ec31 fix(deps): update dependency @babel/types to v7.26.5 (#304)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 15:44:21 +01:00
renovate[bot] 7670c37685 fix(deps): update dependency react-intersection-observer to v9.14.1 (#318)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 15:18:24 +01:00
renovate[bot] ae41c5629c chore(deps): update rust crate serial_test to 0.10.0 (#314)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 14:07:29 +01:00
renovate[bot] 3e7fa05fce chore(deps): update react monorepo (#301)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 12:54:43 +01:00
renovate[bot] 6833640c47 chore(deps): update node.js to v22.13.0 (#312)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 12:14:33 +01:00
renovate[bot] 18541c45de chore(deps): update pnpm to v9.15.3 (#313)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 12:14:06 +01:00
Marco Pasqualetti 45bb0dd0d2 chore: add PR labeler (#310) 2025-01-13 08:58:26 +01:00
Marco Pasqualetti db0aad2c37 chore(deps): update dependency prettier to v3.4.2 (#306) 2025-01-13 05:04:50 +01:00
Valerio Ageno d55ca12125 fix: dev script (#298) 2025-01-12 18:47:21 +01:00
Valerio Ageno ad2cada94b chore: remove benches folder (#294)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2025-01-12 13:45:20 +01:00
spacecodeur f9545f513b chore: dockerization of the tuono repo! (#276)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2025-01-12 09:54:54 +01:00
Marco Pasqualetti c75d83dc7c ci: add pr title check to enforce a consistent git history (#290) 2025-01-05 10:41:23 +01:00
Valerio Ageno 91236e5533 fix: catch-all server side handler (#287) 2025-01-04 12:07:20 +01:00
Valerio Ageno 1356be248e Revert "fix: catch-all server side handler"
This reverts commit 7e8be92cd5.
2025-01-04 10:33:52 +01:00
Valerio Ageno 7e8be92cd5 fix: catch-all server side handler 2025-01-04 10:33:17 +01:00
Valerio Ageno c3fda4a3a5 doc: update tutorial seo page 2025-01-03 17:20:46 +01:00
Valerio Ageno dfc66cfd50 doc: update tutorial 2025-01-03 17:19:09 +01:00
Marco Pasqualetti 0ef1252e1f feat(packages/tuono-(router|fs-router-vite-plugin)): replace __route with __layout (#283) 2025-01-03 17:08:18 +01:00
Valerio Ageno cf56b3a53e Load template by tag (#284) 2025-01-03 17:08:10 +01:00
Marco Pasqualetti 592f402ffd feat: add support for react 19 (#264)
Co-authored-by: Valerio Ageno <valerioageno@yahoo.it>
2025-01-03 11:00:44 +01:00
renovate[bot] 1dc77fc473 fix(deps): update babel monorepo (#271)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 13:36:04 +01:00
Marco Pasqualetti 09cd772d74 chore: refine eslint ignores and fix format script of tuono-fs-router-vite-plugin (#265) 2025-01-02 13:35:22 +01:00
Marco Pasqualetti 1c69e10e6a docs: update tuono to 0.16.9 (#280) 2025-01-02 10:57:39 +01:00
Marco Pasqualetti 1af18e4c91 chore(renovate): setup a package rule for eslint (#279) 2025-01-02 10:19:28 +01:00
renovate[bot] a3709609c5 chore(deps): update dependency @testing-library/react to v16.1.0 (#275)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 10:11:38 +01:00
renovate[bot] 70a79faee0 fix(deps): update dependency @types/node to v22.10.3 (#272)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 09:37:33 +01:00
renovate[bot] 07cb7dae9d chore(deps): update dependency vitest to v2.1.8 (#269)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 09:34:03 +01:00
renovate[bot] 9a1fb9c4a1 chore(deps): update dependency eslint-plugin-react to v7.37.3 (#267)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 09:32:48 +01:00
renovate[bot] a3f7ed7f61 fix(deps): update dependency prettier to v3.4.2 (#273)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 09:28:52 +01:00
renovate[bot] 5302fd2779 chore(deps): update react monorepo (#270)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 08:57:01 +01:00
Marco Pasqualetti ccd4e02b6a docs(local-development): use pnpm instead of turbo (#277) 2025-01-02 08:43:41 +01:00
Valerio Ageno f6593097b6 feat: update version to v0.16.9 2024-12-24 16:20:55 +01:00
gravity 230cd22bff feat(crates/tuono): tuono new - download template by cli version and git tag (#261) 2024-12-24 16:03:29 +01:00
Valerio Ageno f4367f1755 feat: update version to v0.16.8 2024-12-24 11:57:21 +01:00
Marco Pasqualetti 7b7447f9a0 feat(packages/vite-plugins): use named export and consistent names across plugins (#260) 2024-12-24 11:41:58 +01:00
Valerio Ageno d12d8404ec feat(packages/tuono-router): replace zustand with React.Context (#256)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2024-12-24 11:41:45 +01:00
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
400 changed files with 6984 additions and 14571 deletions
+10
View File
@@ -0,0 +1,10 @@
[*]
charset = utf-8
insert_final_newline = true
end_of_line = lf
indent_style = space
indent_size = 2
max_line_length = 80
[*.rs]
indent_size = 4
+15 -45
View File
@@ -1,6 +1,7 @@
name: Bug report
description: File a bug report
title: '[Bug]: '
type: Bug
labels: ['bug']
body:
@@ -32,55 +33,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:
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Ask a question / Get support
url: https://discord.com/invite/khQzPa654B
about: Ask questions and discuss with other community members
@@ -1,6 +1,7 @@
name: Feature request
description: Suggest an idea for this project
title: '[Feature request]: '
type: Feature
labels: ['enhancement']
body:
+34
View File
@@ -0,0 +1,34 @@
# @see https://github.com/actions/labeler
rust:
- changed-files:
- any-glob-to-any-file: ['crates/**', 'Cargo.toml']
typescript:
- changed-files:
- any-glob-to-any-file:
[
'devtools/**',
'packages/**',
'package.json',
'pnpm-*.yaml',
'eslint.config.js',
'tsconfig.json',
]
'CI/CD':
- changed-files:
- any-glob-to-any-file: ['.github/**']
'repo maintenance':
- changed-files:
- any-glob-to-any-file:
[
'devtools/**',
'.npmrc',
'.nvmrc',
'.prettierrc',
'.prettierignore',
'renovate.json',
'turbo.json',
]
+19 -8
View File
@@ -1,13 +1,24 @@
## Context & Description
<!--
👋 Thank you for your Pull Request 🙏
-->
### Checklist
- [ ] I have read [Contributing > Pull requests](https://tuono.dev/documentation/contributing/pull-requests)
### Related issue
Fixes #0
<!-- Replace the content with "None" if you haven't an issue to link -->
### Overview
<!--
Thank you for your Pull Request.
Explain the context and why you're making that change. What is the problem
you're trying to solve? If a new feature is being added, describe the intended
use case that feature fulfills.
Explain the context and why you're making that change.
What is the problem you're trying to solve?
If a new feature is being added,
describe the intended use case that feature fulfills.
Bug fixes and new features should include tests.
Contributors guide: https://github.com/Valerioageno/tuono/blob/main/CONTRIBUTING.md
-->
-65
View File
@@ -1,65 +0,0 @@
name: Documentation Website CI
on:
push:
branches:
- '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:
build:
name: Check build
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install NodeJS Dependencies
uses: ./.github/actions/install-node-dependencies
- name: Setup rust
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install tuono
run: cargo install tuono@0.16.4
- name: Build project
working-directory: ./apps/documentation
run: tuono build --static
code_check:
name: Check format, lint and types
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install NodeJS Dependencies
uses: ./.github/actions/install-node-dependencies
- name: Check formatting
run: pnpm docs:format:check
- name: Lint
run: pnpm docs:lint
- name: Types
run: pnpm docs:types
@@ -1,47 +0,0 @@
name: Deploy documentation website on AWS S3
on:
push:
branches:
- 'main'
paths:
- '.github/**'
- 'apps/documentation/**'
jobs:
deploy-documentation:
name: Deploy
runs-on: ubuntu-latest
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install NodeJS Dependencies
uses: ./.github/actions/install-node-dependencies
- name: Setup rust
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install tuono
run: cargo install tuono@0.16.4
- name: Build project
working-directory: ./apps/documentation
run: tuono build --static
- name: Deploy
uses: reggionick/s3-deploy@v4
with:
folder: apps/documentation/out/static
bucket: tuono-documentation
bucket-region: eu-west-3
dist-id: ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }}
invalidation: /
delete-removed: true
no-cache: true
private: true
files-to-include: '{.*/**,**}'
+61
View File
@@ -0,0 +1,61 @@
name: Docker CI
# If we need support more OS refer to
# https://github.com/tuono-labs/tuono/pull/332#issuecomment-2614439692
on:
push:
branches:
- 'main'
pull_request:
concurrency:
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
build_and_test:
name: Build and Test Docker Image on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build and Run Docker Compose
run: |
docker compose -f docker/compose.yml up --build -d
- name: Check if the container is running
run: |
docker ps --filter "name=tuono-source-container" --format "table {{.Names}}\t{{.Status}}"
if ! docker ps --filter "name=tuono-source-container" --format "{{.Status}}" | grep -q "Up"; then
echo "Error: Container 'tuono-source-container' is not running."
exit 1
fi
- name: Check commands inside container
run: |
docker exec tuono-source-container pnpm --version || { echo "Error: pnpm is not available."; exit 1; }
docker exec tuono-source-container tuono --version || { echo "Error: tuono is not available."; exit 1; }
- name: Run cargo test
run: docker exec tuono-source-container cargo test
- name: Install node dependencies in each tuono's sub-projets
run: docker exec tuono-source-container bash -c 'for dir in /tuono/packages/*; do [ -d "$dir" ] && (cd "$dir" && yes | pnpm install --frozen-lockfile); done'
- name: Run pnpm test
run: docker exec tuono-source-container pnpm test
- name: Tear Down Docker Compose
if: always()
run: |
docker compose -f docker/compose.yml down --volumes
+61
View File
@@ -0,0 +1,61 @@
name: E2E CI
on:
push:
branches:
- 'main'
pull_request:
concurrency:
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
e2e:
name: E2E Test (${{ matrix.os }})
strategy:
fail-fast: true
matrix:
os:
- 'ubuntu-latest'
- 'macos-latest'
- 'windows-latest'
runs-on: ${{ matrix.os }}
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install NodeJS Dependencies
uses: ./.github/actions/install-node-dependencies
- name: Setup rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install Playwright Browsers
run: pnpm exec playwright install
- name: Run Playwright E2E tests
run: pnpm test:e2e
ci_ok:
name: E2E CI OK
runs-on: ubuntu-latest
timeout-minutes: 1
if: always()
needs: [e2e]
env:
FAILURE: ${{ contains(join(needs.*.result, ','), 'failure') }}
CANCELLED: ${{ contains(join(needs.*.result, ','), 'cancelled') }}
steps:
- name: Check for failure or cancelled jobs result
shell: bash
run: |
echo "Failure: $FAILURE - Cancelled: $CANCELLED"
if [ "$FAILURE" = "false" ] && [ "$CANCELLED" = "false" ]; then
exit 0
else
exit 1
fi
+27
View File
@@ -0,0 +1,27 @@
name: 'PR Labeler'
# @see https://github.com/actions/labeler
# @see .github/labeler.yml
on:
- pull_request_target
jobs:
labeler:
name: 'Manage labels'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
# Add checkout step to load labeler.yml locally,
# rather than from an API request
- name: Checkout code
uses: actions/checkout@v4
- uses: actions/labeler@v5
with:
sync-labels: true
+61
View File
@@ -0,0 +1,61 @@
name: 'PR Title Checker'
# This workflow ensures that PR titles adhere to a pattern
# similar to conventional commits standard
on:
pull_request:
types:
- opened
- edited
- synchronize
- labeled
- unlabeled
env:
PR_TITLE: ${{ github.event.pull_request.title }}
jobs:
check_pr_title:
name: 'PR Title Checker'
runs-on: ubuntu-latest
steps:
- name: Check type
id: type
# @warning Keep in sync with https://github.com/tuono-labs/tuono-documentation/blob/main/src/routes/documentation/contributing/pull-requests.mdx
run: |
VALID_COMMIT_TYPES="chore|ci|docs|feat|fix|refactor|test"
REGEX="^(${VALID_COMMIT_TYPES})(\(.*\))?!?: .*"
if ! [[ $PR_TITLE =~ $REGEX ]]; then
echo "::error title=Type::The title has an incorrect type. Valid types are ${VALID_COMMIT_TYPES}"
echo "result=fail" >> "$GITHUB_OUTPUT"
else
echo "::notice title=Type::The title is using a valid type"
echo "result=ok" >> "$GITHUB_OUTPUT"
fi
- name: Check length
id: length
run: |
MAX_LENGTH=100
LENGTH=${#PR_TITLE}
if [ $LENGTH -gt $MAX_LENGTH ]; then
echo "::error title=Length::The title is longer than $MAX_LENGTH characters"
echo "result=fail" >> "$GITHUB_OUTPUT"
else
echo "::notice title=Length::The title length is within the maximum value of $MAX_LENGTH characters"
echo "result=ok" >> "$GITHUB_OUTPUT"
fi
- name: Check result
env:
FAILURE: ${{ contains(join(steps.*.outputs.result, ','), 'fail') }}
run: |
echo "Failure: $FAILURE"
if [ "$FAILURE" = "false" ]; then
exit 0
else
exit 1
fi
+26 -8
View File
@@ -8,16 +8,14 @@ on:
push:
branches:
- 'main'
paths:
- '.github/**'
- '*'
pull_request:
paths:
- '.github/**'
- '*'
concurrency:
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
build-and-test:
format:
name: Check format
runs-on: ubuntu-latest
timeout-minutes: 15
@@ -30,4 +28,24 @@ jobs:
uses: ./.github/actions/install-node-dependencies
- name: Test project
run: pnpm repo:root:format:check
run: pnpm repo:root:format
ci_ok:
name: Repo root CI OK
runs-on: ubuntu-latest
timeout-minutes: 1
if: always()
needs: [format]
env:
FAILURE: ${{ contains(join(needs.*.result, ','), 'failure') }}
CANCELLED: ${{ contains(join(needs.*.result, ','), 'cancelled') }}
steps:
- name: Check for failure or cancelled jobs result
shell: bash
run: |
echo "Failure: $FAILURE - Cancelled: $CANCELLED"
if [ "$FAILURE" = "false" ] && [ "$CANCELLED" = "false" ]; then
exit 0
else
exit 1
fi
@@ -4,15 +4,11 @@ on:
push:
branches:
- 'main'
paths:
- '.github/**'
- 'Cargo.toml'
- 'crates/**'
pull_request:
paths:
- '.github/**'
- 'Cargo.toml'
- 'crates/**'
concurrency:
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
CARGO_TERM_COLOR: always
@@ -40,6 +36,7 @@ jobs:
os: 'ubuntu-latest'
runs-on: ${{ matrix.os }}
timeout-minutes: 15
steps:
- name: Checkout code
@@ -56,6 +53,7 @@ jobs:
lint_and_fmt:
name: Check format and lint rust crates
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
@@ -66,3 +64,23 @@ jobs:
- run: cargo fmt --all -- --check
- run: cargo clippy -- -D warnings
ci_ok:
name: Rust CI OK
runs-on: ubuntu-latest
timeout-minutes: 1
if: always()
needs: [build_and_test, lint_and_fmt]
env:
FAILURE: ${{ contains(join(needs.*.result, ','), 'failure') }}
CANCELLED: ${{ contains(join(needs.*.result, ','), 'cancelled') }}
steps:
- name: Check for failure or cancelled jobs result
shell: bash
run: |
echo "Failure: $FAILURE - Cancelled: $CANCELLED"
if [ "$FAILURE" = "false" ] && [ "$CANCELLED" = "false" ]; then
exit 0
else
exit 1
fi
+4
View File
@@ -7,6 +7,10 @@ on:
pull_request:
concurrency:
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
spell_checking:
name: LanguageTool
@@ -4,26 +4,15 @@ on:
push:
branches:
- '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/**'
concurrency:
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
build_and_test:
name: Check build and test
timeout-minutes: 15
strategy:
fail-fast: true
@@ -34,6 +23,7 @@ jobs:
- 'windows-latest'
runs-on: ${{ matrix.os }}
timeout-minutes: 15
steps:
- name: Checkout code
@@ -61,10 +51,30 @@ jobs:
uses: ./.github/actions/install-node-dependencies
- name: Check formatting
run: pnpm format:check
run: pnpm run format
- name: Lint
run: pnpm lint
run: pnpm run lint
- name: Types
run: pnpm types
- name: Typecheck
run: pnpm run typecheck
ci_ok:
name: Typescript CI OK
runs-on: ubuntu-latest
timeout-minutes: 1
if: always()
needs: [build_and_test, lint_and_fmt]
env:
FAILURE: ${{ contains(join(needs.*.result, ','), 'failure') }}
CANCELLED: ${{ contains(join(needs.*.result, ','), 'cancelled') }}
steps:
- name: Check for failure or cancelled jobs result
shell: bash
run: |
echo "Failure: $FAILURE - Cancelled: $CANCELLED"
if [ "$FAILURE" = "false" ] && [ "$CANCELLED" = "false" ]; then
exit 0
else
exit 1
fi
+4 -1
View File
@@ -8,6 +8,8 @@ dist/
examples/*/pnpm-lock.yaml
vite.config.ts.timestamp*
## Rust related ignores
# Generated by Cargo
@@ -26,4 +28,5 @@ examples/*/pnpm-lock.yaml
*.pdb
# Files, generated by the code editors
.vscode/
.vscode/
+1 -1
View File
@@ -1 +1 @@
22.11.0
22.13.1
+5 -1
View File
@@ -3,4 +3,8 @@ pnpm-lock.yaml
dist
.tuono
packages/lazy-fn-vite-plugin/tests/sources/*
vite.config.ts.timestamp-*
packages/tuono-lazy-fn-vite-plugin/tests/sources/*
e2e/fixtures/*/test-results
+9
View File
@@ -0,0 +1,9 @@
* @Valerioageno @marcalexiei
# Rust
/crates/ @Valerioageno
/Cargo.toml @Valerioageno
# Misc
/.github/ @marcalexiei
+1 -3
View File
@@ -23,6 +23,4 @@ Consider [opening a new issue](https://github.com/tuono-labs/tuono/issues/new/ch
**Did you write a patch that fixes a bug?**
- Open a new GitHub pull request with the patch.
- Ensure the PR description clearly describes the problem and solution. Include the relevant issue number, if applicable.
- The pull requests must pass all the CI pipelines
[Visit the documentation to learn how to open a pull request](https://tuono.dev/documentation/contributing/pull-requests)
+9 -8
View File
@@ -1,14 +1,15 @@
[workspace]
resolver = "2"
members = [
"crates/tuono",
"crates/tuono_lib",
"crates/tuono_lib_macros",
"crates/tuono",
"crates/tuono_internal",
"crates/tuono_lib",
"crates/tuono_lib_macros"
]
exclude = [
"apps/documentation",
"examples/with-mdx",
"examples/tuono-app",
"examples/tuono-tutorial",
"benches/tuono"
"examples/with-mdx",
"examples/tuono-app",
"examples/tuono-tutorial",
"examples/with-tailwind",
"e2e/fixtures/base",
]
+15 -32
View File
@@ -24,51 +24,34 @@ Some of its features are:
- 🧬 Server Side Rendering
- 🔥 Hot Module Reload
## 📖 Documentation
## Documentation
The [documentation](https://tuono.dev/) is available on
[tuono.dev](https://tuono.dev/).
## Introduction
## Installation
**NodeJs/Deno/Bun are the only runtimes that allow a React app to be full-stack right? (no)**
Available on MacOS, Linux and Windows.
Tuono is a full-stack React framework with the server side written in Rust.
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.
**Rust is a hard language, then writing server-side code is hard as well, right? (no again)**
Tuono provides a collection of utilities to handle the server side code seamlessly with the React code.
Each server-side route is managed in a separate file alongside the React route. The routing is handled
by Tuono based on the files defined within the `./src/routes` directory.
**How is Tuono different from Next.js?**
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/tuono-labs/tuono/tree/main/benches)).
```sh
cargo install tuono
```
## Getting started
Tuono is the CLI that provides all the needed commands to handle the full-stack project.
To download it is required [cargo](https://doc.rust-lang.org/cargo/), which is the [rust](https://www.rust-lang.org/)
package manager.
Create a new project with:
Then run `cargo install tuono`.
```sh
tuono new [PROJECT_NAME]
```
To list all the available commands, run `tuono -h`
> You can optionally pass the --template (or -t) flag to directly start from a [template](https://github.com/tuono-labs/tuono/tree/main/examples)
To create a new project, run `tuono new [NAME]` (optionally, you can pass the `--template` (or `-t`) flag - check the
[examples](https://github.com/tuono-labs/tuono/tree/main/examples) folder).
Now just install the dependencies with you favourite JS package manager (i.e. `npm install`) and start the development environment
Then, to run the local development environment, install the JS dependencies (`npm install`) and run inside the project folder `tuono dev`
Finally, when the project is ready to be deployed, just run `tuono build` to create the final React assets
and to set the server project in `production` mode.
Now to execute it just run `cargo run --release`.
```sh
tuono dev
```
## Contributing
-1
View File
@@ -1 +0,0 @@
# Stadalone apps folder
-15
View File
@@ -1,15 +0,0 @@
[package]
name = "tuono"
version = "0.0.1"
edition = "2021"
[[bin]]
name = "tuono"
path = ".tuono/main.rs"
[dependencies]
tuono_lib = "0.16.4"
glob = "0.3.1"
time = { version = "0.3", features = ["macros"] }
serde = { version = "1.0.202", features = ["derive"] }
-7
View File
@@ -1,7 +0,0 @@
# Tuono starter
This is the starter tuono project. To download it run in your terminal:
```shell
$ tuono new [NAME]
```
-32
View File
@@ -1,32 +0,0 @@
{
"name": "documentation",
"description": "The react/rust fullstack framework documentation",
"version": "0.0.1",
"private": true,
"type": "module",
"scripts": {
"lint": "eslint .",
"format": "prettier --write --ignore-unknown .",
"format:check": "prettier --check --ignore-unknown .",
"types": "tsc --noEmit"
},
"dependencies": {
"@mantine/code-highlight": "7.14.2",
"@mantine/core": "7.14.2",
"@mantine/hooks": "7.14.2",
"@mdx-js/react": "3.1.0",
"@tabler/icons-react": "3.22.0",
"clsx": "2.1.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"tuono": "npm:tuono@0.16.4"
},
"devDependencies": {
"@mdx-js/rollup": "3.1.0",
"@types/react": "18.3.13",
"@types/react-dom": "18.3.0",
"postcss": "8.4.49",
"postcss-preset-mantine": "1.17.0",
"postcss-simple-vars": "7.0.1"
}
}
-14
View File
@@ -1,14 +0,0 @@
export default {
plugins: {
'postcss-preset-mantine': {},
'postcss-simple-vars': {
variables: {
'mantine-breakpoint-xs': '36em',
'mantine-breakpoint-sm': '48em',
'mantine-breakpoint-md': '62em',
'mantine-breakpoint-lg': '75em',
'mantine-breakpoint-xl': '88em',
},
},
},
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

@@ -1,18 +0,0 @@
{
"polyfills": [
{
"identifier": "HTMLElement",
"import": {
"package": "@lit-labs/ssr-dom-shim",
"name": "HTMLElement"
}
},
{
"identifier": "customElements",
"import": {
"package": "@lit-labs/ssr-dom-shim",
"name": "customElements"
}
}
]
}
@@ -1,19 +0,0 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
@@ -1,84 +0,0 @@
import type { JSX } from 'react'
import { Breadcrumbs, Button } from '@mantine/core'
import { Link, Head } from 'tuono'
import { IconChevronRight, IconBolt } from '@tabler/icons-react'
interface Breadcrumb {
href?: string
label: string
}
interface BreadcrumbsProps {
breadcrumbs: Array<Breadcrumb>
}
export default function TuonoBreadcrumbs({
breadcrumbs = [],
}: BreadcrumbsProps): JSX.Element {
return (
<>
<Head>
<script type="application/ld+json">
{`{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Tuono - The React/Rust fullstack framework",
"item": "https://tuono.dev"
}${breadcrumbs.length > 0 ? ',' : ''}
${breadcrumbs
.map((br, i) =>
JSON.stringify({
'@type': 'ListItem',
position: i + 3,
name: br.label,
item: br.href ? `https://tuono.dev${br.href}` : undefined,
}),
)
.join(',')}]
}
`}
</script>
</Head>
<Breadcrumbs
separator={<IconChevronRight size="1.1rem" stroke={1.5} />}
mb="md"
mt="md"
>
<Button href="/" component={Link} variant="subtle" radius="xl" p={5}>
<IconBolt />
</Button>
{breadcrumbs.map((br) => (
<BreadcrumbElement href={br.href} label={br.label} key={br.label} />
))}
</Breadcrumbs>
</>
)
}
interface BreadcrumbElementProps {
href?: string
label: string
}
export function BreadcrumbElement({
href,
label,
}: BreadcrumbElementProps): JSX.Element {
if (href) {
return (
<Button component={Link} href={href} variant="subtle" radius="xl" px={10}>
{label}
</Button>
)
}
return (
<Button component="span" variant="light" radius="xl">
{label}
</Button>
)
}
@@ -1,5 +0,0 @@
import Breadcrumbs, { BreadcrumbElement } from './breadcrumbs'
export default Breadcrumbs
export { BreadcrumbElement as Element }
@@ -1,24 +0,0 @@
import type { JSX } from 'react'
import { Button } from '@mantine/core'
import { IconEdit } from '@tabler/icons-react'
import { useRouter } from 'tuono'
const GITHUB_URL =
'https://github.com/tuono-labs/tuono/tree/main/apps/documentation/src/routes'
export default function EditPage(): JSX.Element {
const { pathname } = useRouter()
return (
<Button
p={0}
mt={60}
component="a"
variant="transparent"
leftSection={<IconEdit />}
target="_blank"
href={GITHUB_URL.concat(pathname).concat('.mdx')}
>
Edit page
</Button>
)
}
@@ -1,3 +0,0 @@
import EditPage from './edit-page'
export default EditPage
@@ -1,54 +0,0 @@
import type { JSX } from 'react'
import {
Button,
Center,
Container,
CopyButton,
Group,
rem,
Text,
Title,
} from '@mantine/core'
import { IconCopy, IconCheck } from '@tabler/icons-react'
import { Link } from 'tuono'
export default function Hero(): JSX.Element {
return (
<Container size={1100} my={100}>
<Center>
<Title order={1}>Superfast fullstack react framework</Title>
</Center>
<Center mt={50}>
<Text fz="18px">
The technologies we love seamlessly working together to unleash the
<strong> highest web performance</strong> ever met on React
</Text>
</Center>
<Center mt={50}>
<Group>
<Button component={Link} href="/documentation" size="lg">
Get Started
</Button>
<CopyButton value="cargo install tuono">
{({ copied, copy }) => (
<Button
onClick={copy}
size="lg"
style={{ border: 'solid 1px var(--mantine-color-violet-1)' }}
color="gray"
leftSection="$ cargo install tuono"
rightSection={
copied ? (
<IconCheck style={{ width: rem(20) }} />
) : (
<IconCopy style={{ width: rem(20) }} />
)
}
/>
)}
</CopyButton>
</Group>
</Center>
</Container>
)
}
@@ -1,3 +0,0 @@
import Hero from './hero'
export default Hero
@@ -1,3 +0,0 @@
import MdxProvider from './mdx-provider'
export default MdxProvider
@@ -1,6 +0,0 @@
import type { JSX } from 'react'
import { Text, type TextProps } from '@mantine/core'
export default function MdxBold(props: TextProps): JSX.Element {
return <Text fw={700} {...props} />
}
@@ -1,3 +0,0 @@
import MdxCode from './mdx-code'
export default MdxCode
@@ -1,8 +0,0 @@
import type { JSX, HTMLAttributes } from 'react'
import { Code } from '@mantine/core'
export default function MdxCode(
props: HTMLAttributes<HTMLPreElement>,
): JSX.Element {
return <Code {...props} style={{ fontSize: 'inherit' }} />
}
@@ -1,3 +0,0 @@
import MdxLink from './mdx-link'
export default MdxLink
@@ -1,36 +0,0 @@
import type { JSX, AnchorHTMLAttributes } from 'react'
import { Button } from '@mantine/core'
import { Link } from 'tuono'
import { IconExternalLink } from '@tabler/icons-react'
export default function MdxLink(
props: AnchorHTMLAttributes<HTMLAnchorElement>,
): JSX.Element {
if (props.href?.startsWith('http') || props.href?.startsWith('mailto')) {
return (
<Button
component="a"
{...props}
target="_blank"
rightSection={
<IconExternalLink size="16px" style={{ marginLeft: -5 }} />
}
variant="transparent"
style={{ height: '20px' }}
mt={-2}
p={0}
/>
)
}
return (
<Button
component={Link}
{...props}
target="_blank"
variant="transparent"
style={{ height: '20px' }}
mt={-2}
p={0}
/>
)
}
@@ -1,3 +0,0 @@
import MdxPre from './mdx-pre'
export default MdxPre
@@ -1,5 +0,0 @@
.pre {
code {
font-size: 14px;
}
}
@@ -1,24 +0,0 @@
import type { JSX } from 'react'
import { CodeHighlight } from '@mantine/code-highlight'
import styles from './mdx-pre.module.css'
interface PreProps {
children: {
props: {
children: string
className?: string
}
}
}
export default function MdxPre({ children }: PreProps): JSX.Element {
return (
<CodeHighlight
className={styles.pre}
code={children.props.children || ''}
style={{ borderRadius: 8, fontWeight: 100 }}
language={children.props.className?.replace('language-', '')}
/>
)
}
@@ -1,38 +0,0 @@
import type { JSX, ReactNode } from 'react'
import { MDXProvider } from '@mdx-js/react'
import MdxLink from './mdx-link'
import MdxPre from './mdx-pre'
import MdxQuote from './mdx-quote'
import MdxCode from './mdx-code'
import { h } from './mdx-title'
import MdxBold from './mdx-bold/mdx-bold'
interface MdxProviderProps {
children: ReactNode
}
export default function MdxProvider({
children,
}: MdxProviderProps): JSX.Element {
return (
<MDXProvider
components={{
a: MdxLink,
// @ts-expect-error: useless finding the correct props types
pre: MdxPre,
blockquote: MdxQuote,
code: MdxCode,
h1: h(1),
h2: h(2),
h3: h(3),
h4: h(4),
h5: h(5),
h6: h(6),
strong: MdxBold,
}}
>
{children}
</MDXProvider>
)
}
@@ -1,3 +0,0 @@
import MdxQuote from './mdx-quote'
export default MdxQuote
@@ -1,21 +0,0 @@
import type { JSX, HTMLAttributes } from 'react'
import { Blockquote, Space } from '@mantine/core'
export default function MdxQuote(
props: HTMLAttributes<HTMLQuoteElement>,
): JSX.Element {
return (
<>
<Blockquote
color="violet"
py={1}
px={20}
mt={30}
iconSize={30}
{...props}
style={{ borderRadius: 8 }}
/>
<Space h="md" />
</>
)
}
@@ -1,5 +0,0 @@
import MdxTitle, { h } from './mdx-title'
export default MdxTitle
export { h }
@@ -1,21 +0,0 @@
import type { JSX } from 'react'
import { Title, type TitleProps } from '@mantine/core'
export default function MdxTitle(props: TitleProps): JSX.Element {
return (
<Title
data-heading={props.children}
data-order={props.order}
mt={20}
{...props}
// eslint-disable-next-line @typescript-eslint/no-base-to-string
id={String(props.children ?? '')
.toLowerCase()
.replaceAll(' ', '-')}
/>
)
}
export const h =
(order: 1 | 2 | 3 | 4 | 5 | 6) =>
(props: TitleProps): JSX.Element => <MdxTitle order={order} {...props} />
@@ -1,3 +0,0 @@
import MetaTags from './meta-tags'
export default MetaTags
@@ -1,46 +0,0 @@
import type { ReactNode } from 'react'
import { Head } from 'tuono'
interface MetaTagsProps {
title: string
description?: string
type?: 'website' | 'article' | 'book' | 'video.movie' | 'music.song'
canonical: string
}
export default function MetaTags({
title,
description,
type = 'article',
canonical,
}: MetaTagsProps): ReactNode {
return (
<Head>
<title>{title}</title>
<link rel="canonical" href={canonical} />
<meta
name="description"
content={
description ??
'The technologies we love seamlessly working together to unleash the highest web performance ever met on React'
}
/>
<meta
name="keywords"
content="ReactJs, Typescript, Rust, JavaScript, Fullstack framework, Web development"
/>
<meta property="og:title" content={title} />
<meta property="og:type" content={type} />
<meta property="og:image" content="https://tuono.dev/og-cover.png" />
<meta property="og:url" content={canonical} />
<meta property="og:description" content={description} />
<meta property="og:site_name" content="Tuono" />
<meta property="og:locale" content="en_US" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@tuono_dev" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content="https://tuono.dev/og-cover.png" />
</Head>
)
}
@@ -1,35 +0,0 @@
import type { JSX } from 'react'
import { Flex, ActionIcon, Group } from '@mantine/core'
import { IconBrandGithub, IconBrandDiscord } from '@tabler/icons-react'
import ThemeBtn from '../theme-btn'
export default function Actions(): JSX.Element {
return (
<Flex gap={8}>
<Group gap={8}>
<ActionIcon
variant="default"
size="lg"
aria-label="Check the project on github"
href="https://github.com/tuono-labs/tuono"
target="_blank"
component="a"
>
<IconBrandGithub />
</ActionIcon>
<ActionIcon
variant="default"
size="lg"
aria-label="Join the Tuono's community on Discord!"
href="https://discord.com/invite/khQzPa654B"
target="_blank"
component="a"
>
<IconBrandDiscord />
</ActionIcon>
</Group>
<ThemeBtn />
</Flex>
)
}
@@ -1,3 +0,0 @@
import Navbar from './navbar'
export default Navbar
@@ -1,33 +0,0 @@
import type { JSX } from 'react'
import { AppShell, Box, Burger, Button, Flex } from '@mantine/core'
import { Link } from 'tuono'
import Actions from './actions'
interface NavbarProps {
toggle: () => void
}
export default function Navbar({ toggle }: NavbarProps): JSX.Element {
return (
<AppShell.Header p="sm">
<Flex justify="space-between">
<Button
component={Link}
href="/"
variant="transparent"
p={0}
fz={28}
hiddenFrom="sm"
>
Tuono
</Button>
<Box />
<Flex align="center" gap={8}>
<Actions />
<Burger onClick={toggle} hiddenFrom="sm" size="sm" />
</Flex>
</Flex>
</AppShell.Header>
)
}
@@ -1,3 +0,0 @@
import NavigationButtons from './navigation-buttons'
export default NavigationButtons
@@ -1,63 +0,0 @@
import type { JSX } from 'react'
import { Box, Button, Text, Title, Flex } from '@mantine/core'
import { Link } from 'tuono'
import { IconArrowRight, IconArrowLeft } from '@tabler/icons-react'
interface NavigationButton {
href: string
title: string
}
interface NavigationButtonsProps {
prev?: NavigationButton
next?: NavigationButton
}
export default function NavigationButtons({
prev,
next,
}: NavigationButtonsProps): JSX.Element {
return (
<Flex mt={50} gap={10}>
{prev && <NavigationBtn type="prev" {...prev} />}
{next && <NavigationBtn type="next" {...next} />}
</Flex>
)
}
interface NavigationButtonProps extends NavigationButton {
type: 'next' | 'prev'
}
const NavigationBtn = ({
type,
title,
href,
}: NavigationButtonProps): JSX.Element => {
const heading = type === 'next' ? 'Next' : 'Previous'
const textAlign = type === 'next' ? 'left' : 'right'
const variant = type === 'next' ? 'filled' : 'outline'
return (
<Button
component={Link}
fullWidth
variant={variant}
href={href}
justify="space-between"
h="auto"
rightSection={type === 'next' && <IconArrowRight />}
leftSection={type === 'prev' && <IconArrowLeft />}
p="20"
>
<Box>
<Title component="span" display="block" order={4} style={{ textAlign }}>
{heading}
</Title>
<Text component="span" display="block" style={{ textAlign }}>
{title}
</Text>
</Box>
</Button>
)
}
@@ -1,265 +0,0 @@
import type { ReactNode } from 'react'
interface SidebarElementBase<T extends string> {
type: T
}
type SidebarDivider = SidebarElementBase<'divider'>
interface SidebarLink extends SidebarElementBase<'element'> {
label: string
href: string
children?: Array<SidebarLink>
leftIcon?: ReactNode
}
interface SidebarTitle extends SidebarElementBase<'title'> {
label: string
}
type SidebarElement = SidebarDivider | SidebarLink | SidebarTitle
export const sidebarElements: Array<SidebarElement> = [
{
type: 'element',
label: 'Home',
href: '/',
},
{
type: 'element',
label: 'Getting started',
href: '#focus',
children: [
{
type: 'element',
label: 'Installation',
href: '/documentation/installation',
},
{
type: 'element',
label: 'How is tuono different?',
href: '/documentation/how-is-tuono-different',
},
],
},
{
type: 'element',
label: 'Tutorial',
href: '#focus',
children: [
{
type: 'element',
href: '/documentation/tutorial',
label: 'Intro',
},
{
type: 'element',
href: '/documentation/tutorial/development-setup',
label: 'Development setup',
},
{
type: 'element',
href: '/documentation/tutorial/api-fetching',
label: 'API fetching',
},
{
type: 'element',
href: '/documentation/tutorial/components',
label: 'Components',
},
{
type: 'element',
href: '/documentation/tutorial/dynamic-routes',
label: 'Dynamic routes',
},
{
type: 'element',
href: '/documentation/tutorial/error-handling',
label: 'Error handling',
},
{
type: 'element',
href: '/documentation/tutorial/seo',
label: 'SEO and meta tags',
},
{
type: 'element',
href: '/documentation/tutorial/redirections',
label: 'Redirections',
},
{
type: 'element',
href: '/documentation/tutorial/production',
label: 'Production build',
},
{
type: 'element',
href: '/documentation/tutorial/conclusion',
label: 'Conclusion',
},
],
},
{ type: 'divider' },
{ type: 'title', label: 'Overview' },
{
type: 'element',
label: 'Routing',
href: '#focus',
children: [
{
type: 'element',
label: 'Defining routes',
href: '/documentation/routing/defining-routes',
},
{
type: 'element',
label: 'Dynamic routes',
href: '/documentation/routing/dynamic-routes',
},
{
type: 'element',
label: 'Link and navigation',
href: '/documentation/routing/link-and-navigation',
},
{
type: 'element',
label: 'Pages',
href: '/documentation/routing/pages',
},
{
type: 'element',
label: 'Loading state',
href: '/documentation/routing/loading-state',
},
{
type: 'element',
label: 'Layouts',
href: '/documentation/routing/layouts',
},
{
type: 'element',
label: 'Redirecting',
href: '/documentation/routing/redirecting',
},
],
},
{
type: 'element',
label: 'Rendering',
href: '#focus',
children: [
{
type: 'element',
label: 'Server side rendering',
href: '/documentation/rendering/server-side-rendering',
},
{
type: 'element',
label: 'Static site rendering',
href: '/documentation/rendering/static-site-rendering',
},
],
},
{
type: 'element',
label: 'Styles',
href: '#focus',
children: [
{
type: 'element',
label: 'CSS modules',
href: '/documentation/styles/css-modules',
},
],
},
{
type: 'element',
label: 'Integrations',
href: '#focus',
children: [
{
type: 'element',
label: 'MDX',
href: '/documentation/integrations/mdx',
},
],
},
{
type: 'element',
label: 'Core concepts',
href: '#focus',
children: [
{
type: 'element',
label: 'Multithreading',
href: '/documentation/core-concepts/multithreading',
},
],
},
{ type: 'divider' },
{ type: 'title', label: 'API reference' },
{
type: 'element',
label: 'Components',
href: '#focus',
children: [
{
type: 'element',
label: 'Head',
href: '/documentation/components/head',
},
{
type: 'element',
label: 'Link',
href: '/documentation/components/link',
},
],
},
{
type: 'element',
label: 'Hooks',
href: '#focus',
children: [
{
type: 'element',
label: 'useRouter',
href: '/documentation/hooks/use-router',
},
],
},
{
type: 'element',
label: 'CLI',
href: '/documentation/cli',
},
{
type: 'element',
label: 'Application state',
href: '/documentation/application-state',
},
{
type: 'element',
label: 'tuono.config.ts',
href: '/documentation/configuration',
},
{ type: 'divider' },
{
type: 'element',
label: 'Contributing',
href: '#focus',
leftIcon: '✨',
children: [
{
type: 'element',
label: 'Guildelines',
href: '/documentation/contributing',
},
{
type: 'element',
label: 'Local development',
href: '/documentation/contributing/local-development',
},
],
},
]
@@ -1,3 +0,0 @@
import Sidebar from './sidebar'
export default Sidebar
@@ -1,17 +0,0 @@
.link {
border-radius: 8px;
margin-top: 0.25rem;
line-height: 1rem;
font-weight: 500;
transition: 0.3s;
}
.link span {
font-size: 14px !important;
color: var(--mantine-color-sidebar-gray);
}
.link:hover span,
.active span {
color: var(--mantine-color-sidebar-text-hover);
}
@@ -1,42 +0,0 @@
import type { JSX, ReactNode } from 'react'
import { NavLink, type NavLinkProps } from '@mantine/core'
import clsx from 'clsx'
import { Link, useRouter } from 'tuono'
import { IconChevronRight } from '@tabler/icons-react'
import styles from './sidebar-link.module.css'
interface SidebarLinkProps {
label: string
href: string
onClick?: () => void
children?: ReactNode
}
export default function SidebarLink(
props: SidebarLinkProps & NavLinkProps,
): JSX.Element {
const { pathname } = useRouter()
const isActive = pathname === props.href
const internalProps = {
active: isActive,
className: clsx(styles.link, isActive && styles.active),
rightSection: props.children && (
<IconChevronRight
size="1.2rem"
stroke={1.5}
className="mantine-rotate-rtl"
/>
),
autoContrast: true,
...props,
}
if (props.href.startsWith('#')) {
return <NavLink component="button" {...internalProps} />
}
return <NavLink component={Link} {...internalProps} />
}
@@ -1,106 +0,0 @@
import type { JSX } from 'react'
import {
AppShell,
Badge,
Flex,
Divider,
Title,
Button,
ScrollArea,
Text,
} from '@mantine/core'
import { IconX } from '@tabler/icons-react'
import { useMediaQuery } from '@mantine/hooks'
import { sidebarElements } from './config'
import SidebarLink from './sidebar-link'
interface SidebarProps {
close: () => void
}
function SidebarHeader({ close }: SidebarProps): JSX.Element {
const isSm = useMediaQuery('(min-width: 48em)')
return (
<AppShell.Section>
<Flex mb={20} w="100%" justify="space-between">
<Flex
gap={10}
align="center"
w={isSm ? '100%' : 'auto'}
justify="center"
>
<Title order={3}>Tuono</Title>
<Badge mt={4} size="xs" variant="outline">
Docs
</Badge>
</Flex>
<Button onClick={close} hiddenFrom="sm" variant="transparent" p="0">
<IconX />
</Button>
</Flex>
</AppShell.Section>
)
}
function SidebarElements({ close }: SidebarProps): JSX.Element {
return (
<AppShell.Section component={ScrollArea}>
{sidebarElements.map((el, i) => {
if (el.type === 'divider') {
return <Divider my="md" mx={10} />
}
if (el.type === 'title') {
return (
<Text
size="xs"
fw={700}
fz={12}
pl={12}
py={5}
children={el.label}
/>
)
}
if (el.children?.length) {
return (
<SidebarLink
href={el.href}
label={el.label}
key={i}
leftSection={el.leftIcon}
>
{el.children.map((child, index) => (
<SidebarLink
href={child.href}
label={child.label}
key={index}
onClick={close}
/>
))}
</SidebarLink>
)
}
return (
<SidebarLink
href={el.href}
label={el.label}
onClick={close}
key={i}
/>
)
})}
</AppShell.Section>
)
}
export default function Sidebar({ close }: SidebarProps): JSX.Element {
return (
<AppShell.Navbar p="md">
<SidebarHeader close={close} />
<SidebarElements close={close} />
</AppShell.Navbar>
)
}
@@ -1,38 +0,0 @@
/**
* Component inspired by: https://github.com/mantinedev/mantine/tree/master/apps/mantine.dev/src/components/TableOfContents
*/
export interface Heading {
depth: number
content: string
id: string
getNode: () => HTMLHeadingElement
}
function getHeadingsData(headings: Array<HTMLHeadingElement>): Array<Heading> {
const result: Array<Heading> = []
for (const heading of headings) {
if (heading.id) {
result.push({
depth: parseInt(heading.getAttribute('data-order') || '1', 10),
content: heading.getAttribute('data-heading') || '',
id: heading.id,
getNode: () =>
document.getElementById(heading.id) as HTMLHeadingElement,
})
}
}
return result
}
export function getHeadings(): Array<Heading> {
const root = document.getElementById('mdx-root')
console.log(root)
if (!root) {
return []
}
return getHeadingsData(Array.from(root.querySelectorAll('[data-heading]')))
}
@@ -1,6 +0,0 @@
/*
* Component inspired by: https://github.com/mantinedev/mantine/tree/master/apps/mantine.dev/src/components/TableOfContents
*/
import { TableOfContents } from './table-of-content'
export default TableOfContents
@@ -1,120 +0,0 @@
/*
* Component inspired by: https://github.com/mantinedev/mantine/tree/master/apps/mantine.dev/src/components/TableOfContents
*/
.wrapper {
padding-left: var(--mantine-spacing-md);
position: sticky;
top: var(--mantine-spacing-xl);
right: 0;
padding-top: 55px;
flex: 0 0 calc(var(--docs-table-of-contents-width) - 20px);
width: 200px;
@mixin rtl {
padding-left: 0;
padding-right: var(--mantine-spacing-md);
right: auto;
left: 0;
}
&[data-with-tabs] {
padding-top: 0;
top: calc(var(--mantine-spacing-xl) + 60px);
}
}
.inner {
padding-bottom: var(--mantine-spacing-xl);
padding-left: var(--mantine-spacing-md);
display: flex;
flex-direction: column;
justify-content: space-between;
@mixin rtl {
padding-left: 0;
padding-right: var(--mantine-spacing-md);
}
}
.link {
display: block;
border-left: 1px solid transparent;
padding: 8px var(--mantine-spacing-md);
margin-left: -1px;
padding-left: calc(var(--toc-link-offset) * var(--mantine-spacing-lg));
border-top-right-radius: var(--mantine-radius-sm);
border-bottom-right-radius: var(--mantine-radius-sm);
@mixin light {
color: var(--mantine-color-gray-7);
}
@mixin dark {
color: var(--mantine-color-dark-1);
}
@mixin rtl {
border-left: 0;
border-right: 1px solid transparent;
margin-left: 0;
margin-right: -1px;
border-top-left-radius: var(--mantine-radius-sm);
border-bottom-left-radius: var(--mantine-radius-sm);
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&[data-active] {
border-color: var(--mantine-color-violet-5);
@mixin light {
color: var(--mantine-color-violet-8);
background-color: var(--mantine-color-violet-0);
}
@mixin dark {
color: var(--mantine-color-violet-1);
background-color: #534a6d;
}
}
}
.header {
display: flex;
align-items: center;
margin-bottom: var(--mantine-spacing-md);
}
.title {
margin-left: var(--mantine-spacing-md);
@mixin rtl {
margin-left: 0;
margin-right: var(--mantine-spacing-md);
}
}
.items {
border-left: 1px solid;
@mixin light {
border-color: var(--mantine-color-gray-3) !important;
}
@mixin dark {
border-color: var(--mantine-color-dark-4) !important;
}
@mixin rtl {
border-left: 0;
border-right: 1px solid;
@mixin light {
border-color: var(--mantine-color-gray-3) !important;
}
@mixin dark {
border-color: var(--mantine-color-dark-4) !important;
}
}
}
@@ -1,115 +0,0 @@
/*
* Component inspired by: https://github.com/mantinedev/mantine/tree/master/apps/mantine.dev/src/components/TableOfContents
*/
import type { JSX } from 'react'
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'
interface TableOfContentsProps {
withTabs: boolean
}
function getActiveElement(rects: Array<DOMRect>): number {
if (rects.length === 0) {
return -1
}
const closest = rects.reduce(
(acc, item, index) => {
if (Math.abs(acc.position) < Math.abs(item.y)) {
return acc
}
return {
index,
position: item.y,
}
},
{ index: 0, position: rects[0].y },
)
return closest.index
}
export function TableOfContents({
withTabs,
}: TableOfContentsProps): JSX.Element | null {
const [active, setActive] = useState(0)
const [headings, setHeadings] = useState<Array<Heading>>([])
const headingsRef = useRef<Array<Heading>>([])
const router = useRouter()
const filteredHeadings = headings.filter((heading) => heading.depth > 1)
const handleScroll = (): void => {
setActive(
getActiveElement(
headingsRef.current.map((d) => d.getNode().getBoundingClientRect()),
),
)
}
useEffect(() => {
const _headings = getHeadings()
headingsRef.current = _headings
setHeadings(_headings)
setActive(
getActiveElement(
_headings.map((d) => d.getNode().getBoundingClientRect()),
),
)
window.addEventListener('scroll', handleScroll)
return (): void => window.removeEventListener('scroll', handleScroll)
}, [router.pathname])
if (filteredHeadings.length === 0) {
return null
}
const items = filteredHeadings.map((heading, index) => (
<Text
key={heading.id}
component={Link}
fz="sm"
p={10}
className={classes.link}
mod={{ active: active === index }}
href={`#${heading.id}`}
__vars={{ '--toc-link-offset': `${heading.depth - 1}` }}
>
{heading.content}
</Text>
))
return (
<Box
component="nav"
mod={{ 'with-tabs': withTabs }}
className={classes.wrapper}
>
<div className={classes.inner}>
<div>
<div className={classes.header}>
<IconList
style={{ width: rem(20), height: rem(20) }}
stroke={1.5}
/>
<Text className={classes.title}>Table of contents</Text>
</div>
<ScrollArea.Autosize
mah={`calc(100vh - ${rem(140)})`}
type="never"
offsetScrollbars
>
<div className={classes.items}>{items}</div>
</ScrollArea.Autosize>
</div>
</div>
</Box>
)
}
@@ -1,3 +0,0 @@
import ThemeBtn from './theme-btn'
export default ThemeBtn
@@ -1,24 +0,0 @@
.icon {
width: 22px;
height: 22px;
}
.dark {
@mixin dark {
display: none;
}
@mixin light {
display: block;
}
}
.light {
@mixin light {
display: none;
}
@mixin dark {
display: block;
}
}
@@ -1,31 +0,0 @@
import type { JSX } from 'react'
import {
ActionIcon,
useMantineColorScheme,
useComputedColorScheme,
} from '@mantine/core'
import { IconSun, IconMoon } from '@tabler/icons-react'
import cx from 'clsx'
import classes from './theme-btn.module.css'
export default function ThemeBtn(): JSX.Element {
const { setColorScheme } = useMantineColorScheme()
const computedColorScheme = useComputedColorScheme('light', {
getInitialValueInEffect: true,
})
return (
<ActionIcon
onClick={() =>
setColorScheme(computedColorScheme === 'light' ? 'dark' : 'light')
}
variant="default"
size="lg"
aria-label="Toggle color scheme"
>
<IconSun className={cx(classes.icon, classes.light)} stroke={1.5} />
<IconMoon className={cx(classes.icon, classes.dark)} stroke={1.5} />
</ActionIcon>
)
}
-5
View File
@@ -1,5 +0,0 @@
// declaration.d.ts
declare module '*.css' {
const CSSModule: Record<string, string>
export default CSSModule
}
-136
View File
@@ -1,136 +0,0 @@
import type { ReactNode, JSX } from 'react'
import {
ColorSchemeScript,
createTheme,
MantineProvider,
AppShell,
Container,
} from '@mantine/core'
import type { CSSVariablesResolver } from '@mantine/core'
import { useDisclosure } from '@mantine/hooks'
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'
interface RootRouteProps {
children: ReactNode
}
const theme = createTheme({
primaryColor: 'violet',
primaryShade: { light: 6, dark: 9 },
fontFamily: 'Inter',
respectReducedMotion: true,
radius: {
xs: '8px',
lg: '8px',
xl: '8px',
md: '8px',
sm: '8px',
},
fontSizes: {
// 'xs' | 'sm' | 'md' | 'lg' | 'xl'
xs: '16px',
sm: '16px',
},
colors: {
dark: [
'#d5d7e0',
'#acaebf',
'#8c8fa3',
'#666980',
'#4d4f66',
'#34354a',
'#2b2c3d',
'#1d1e30',
'#0c0d21',
'#01010a',
],
},
headings: {
sizes: {
h1: {
fontSize: '48px',
},
},
},
other: {
sidebarGrayLight: '#495057',
sidebarGrayDark: '#adb5bd',
sidebarTextHoverLight: '#212529',
sidebarTextHoverDark: '#f8f9fa',
},
})
const resolver: CSSVariablesResolver = (th) => ({
variables: {},
light: {
'--mantine-color-sidebar-gray': th.other.sidebarGrayLight as string,
'--mantine-color-sidebar-text-hover': th.other
.sidebarTextHoverLight as string,
},
dark: {
'--mantine-color-sidebar-gray': th.other.sidebarGrayDark as string,
'--mantine-color-sidebar-text-hover': th.other
.sidebarTextHoverDark as string,
},
})
export default function RootRoute({ children }: RootRouteProps): JSX.Element {
const [opened, { toggle }] = useDisclosure()
return (
<>
<Head>
<meta charSet="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon-16x16.png"
/>
<link rel="manifest" href="/site.webmanifest" />
</Head>
<ColorSchemeScript />
<MantineProvider theme={theme} cssVariablesResolver={resolver}>
<AppShell
layout="alt"
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: 'sm',
collapsed: { mobile: !opened },
}}
>
<Navbar toggle={toggle} />
<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,55 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Application state"
canonical="https://tuono.dev/documentation/application-state"
description="Learn how to add features to your Tuono application"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Application state' }]} />
# Application state
The main reason Tuono is fast is that it loads just the features that are needed for the project.
To define them, you need to fill the `ApplicationState` struct in the `./src/app.rs` file, and
they will be automatically available in all the handlers you will define across the application.
```rs
// src/app.rs
#[derive(Clone)]
pub struct ApplicationState {
pub website_name: String,
pub base_path: String
}
pub fn main() -> ApplicationState {
let website_name = "tuono".to_string();
let base_path = "http://localhost:3000".to_string();
ApplicationState {
website_name,
base_path
}
}
```
> For the sake of simplicity here we just enable a `String` but you can add Database connections or
> HTTP clients (take a look [here](/documentation/tutorial/api-fetching) for an example with an HTTP client).
Now the `ApplicationState` is available on all the handlers as following:
```rs
// src/routes/index.rs
#[tuono_lib::handler]
// The first argument always is the Request
// The ApplicationState arguments are optional. You can use just the ones you need
// to use in the handler (with no specific order).
async fn my_handler(req: Request, website_name: String) -> Response {
...
}
```
@@ -1,44 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - CLI"
canonical="https://tuono.dev/documentation/cli"
description="Tuono is the CLI that provides all the needed commands to handle the full-stack project."
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'CLI' }]} />
# CLI
Tuono is the CLI that provides all the needed commands to handle the full-stack project.
> ☝️ Check the [installation](/documentation/installation) page if you haven't installed the
> `tuono` CLI yet.
To list all the available commands, run `tuono -h`.
```bash
The React/Rust full-stack framework
Usage: tuono <COMMAND>
Commands:
dev Start the development environment
build Build the production assets
new Scaffold a new project
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
```
To create a new project, run `tuono new [NAME]` (optionally you can pass the --template (or -t) flag - check the examples' folder).
Then to run the local development environment run inside the project folder `tuono dev`
Finally, when the project will be ready to be deployed just run `tuono build` to create the final React assets and to set the server project in the production mode.
Now to execute it, just run `cargo run --release`.
@@ -1,14 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Head"
canonical="https://tuono.dev/documentation/components/head"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Components' }]} />
# Head
TODO
@@ -1,14 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Link"
canonical="https://tuono.dev/documentation/components/link"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Components' }]} />
# Link
TODO
@@ -1,14 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - tuono.config.ts"
canonical="https://tuono.dev/documentation/configuration"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Configuration' }]} />
# Configuration
TODO
@@ -1,48 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Contributing"
canonical="https://tuono.dev/documentation/contributing"
description="The project is massive - if you like it, do consider contributing!"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: '✨ Contributing' }]} />
# Contributing
## TL;DR
The project is massive - if you like it, do consider contributing!
## Getting started
The `tuono` project can mostly be split into the following subdomains:
- The CLI
- The Rust backend
- The React frontend
- The documentation website (which is written with tuono 🚀)
To check what the knowledge requirements are for each domain, check the
[requirements](#requirements) section below.
Currently, I'm keeping a private dashboard to prioritize new features and bug fixes, but if you
want to propose something, please open a new issue on Github or reach out to me using
my email address [valerioageno@yahoo.it](mailto:valerioageno@yahoo.it). I'm also available
on Twitter (X) DMs `@valerioageno`, [Linkedin](https://www.linkedin.com/in/valerioageno)
and discord `@__v__v__`.
## Requirements
It's not strictly required to know both React (& typescript) and Rust (even though it
would be a great nice to have).
Without taking into account specific cases, we can mostly split the domain requirements by:
- The `CLI` needs Rust knowledge (even though a couple of scenarios might also need Typescript)
- The Backend needs just `Rust`
- The Frontend needs just `React` & `Typescript`
- The documentation website needs just `React` & `Typescript` (or even less, since most of the
code is markdown).
@@ -1,56 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Contributing"
canonical="https://tuono.dev/documentation/contributing"
description="The project is massive - if you like it, do consider contributing!"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: '✨ Contributing' }]} />
# Contributing
## TL;DR
The project is massive - if you like it, do consider contributing!
## Getting started
The `tuono` project can mostly be split into the following subdomains:
- The CLI
- The Rust backend
- The React frontend
- The documentation website (which is written with tuono 🚀)
To check what the knowledge requirements are for each domain, check the
[requirements](#requirements) section below.
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
It's not strictly required to know both React (& typescript) and Rust (even though it
would be a great nice to have).
Without taking into account specific cases, we can mostly split the domain requirements by:
- The `CLI` needs Rust knowledge (even though a couple of scenarios might also need Typescript)
- The Backend needs just `Rust`
- The Frontend needs just `React` & `Typescript`
- The documentation website needs just `React` & `Typescript` (or even less, since most of the
code is markdown).
import NavigationButtons from '../../../components/navigation-buttons'
<NavigationButtons
next={{
title: 'Local development',
href: '/documentation/contributing/local-development',
}}
/>
@@ -1,113 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Contributing - Local development"
canonical="https://tuono.dev/documentation/contributing/local-development"
description="Contribute to Tuono. Learn here how to setup the repository for local development"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs
breadcrumbs={[
{ label: '✨ Contributing', href: '/documentation/contributing' },
{ label: 'Local development' },
]}
/>
# Local development
Thanks for your interest! In this page, you find the instructions to set up `tuono` on your local environment!
## Setup
### Fork and clone repository
After [forking the repo on GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo):
```sh
git clone https://github.com/<your-name-here>/tuono
cd tuono
```
### Rust tool chain
Install the Rust programming language tool chain (`rust` and `cargo`).
Follows instructions in the official [docs](https://rustup.rs/)
### Node.js — runtime
Install `Node.js`.
You can follow the instructions from the [Node official site](https://nodejs.org/en/download/package-manager)
> 💡 This project has a `.nvmrc` file to specify the node version used in development.
>
> Consider to use [nvm](https://github.com/nvm-sh/nvm) so you can run
>
> ```sh
> nvm use
> ```
>
> to simply pick up the correct version!
### Node.js — package manager
We use [`pnpm`](https://pnpm.io) as Node.js package manager.
You can see which version of yarn we use by checking the `packageManager` field in the root `package.json`.
### Pre-flight checks
To check that everything is working properly, run:
```sh
turbo run check-all
cargo build
```
## Tuono development
1. Start tuono frontend build using
```sh
turbo run dev
```
2. In another terminal run
```sh
cargo build
```
To automatically rebuild crates on code change, consider using `cargo-watch` crate
```sh
cargo watch -x build -w crates/
```
3. You can now use the binary inside `/target/debug/tuono` in another folder on your local machine
> Consider adding an alias to your shell setup file
>
> ```sh
> alias t="/path-to-repo/target/debug/tuono"
> ```
## Documentation development
1. Change the current working directory to the documentation folder:
```sh
cd apps/documentation
```
2. Run
```sh
tuono dev
```
3. Open the localhost URL.
> On the documentation remember that `tuono` `npm` package is installed from the registry and
> it is not linked to the repository.
@@ -1,14 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Multithreading"
canonical="https://tuono.dev/documentation/core-concepts/multithreading"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Core concepts' }]} />
# Multithreading
TODO
@@ -1,14 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - useRouter"
canonical="https://tuono.dev/documentation/hooks/use-router"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Hooks' }]} />
# useRouter
TODO
@@ -1,14 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - tuono.config.ts"
canonical="https://tuono.dev/documentation/how-is-tuono-different"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'How is tuono different?' }]} />
# How is tuono different?
TODO
@@ -1,57 +0,0 @@
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Installation"
canonical="https://tuono.dev/documentation/installation"
description="Tuono is a development environment built in rust and typescript that outputs a website written in both the languages"
/>
<Breadcrumbs breadcrumbs={[{ label: 'Installation' }]} />
# Installation
## Requirements
`Tuono` is a development environment built in rust and typescript that outputs a website written in both the
languages. Since then, you need the following tools installed on your computer to work with it:
- `Rust` - The Rust programming language tool chain (Go [here](https://rustup.rs/) for installing both `rust` and `cargo`)
- `Cargo` - The rust package manager
- `NodeJs` - The JavaScript runtime (Go [here](https://nodejs.org/en/download/package-manager) for installing both `nodejs` and `npm`)
- `npm` or `yarn` or `pnpm` - A javaScript package manager
> Node.js is needed just for the development environment. The final output will run on a Rust server.
## Installation
The tuono `CLI` is hosted on [crates.io](https://crates.io/crates/tuono) to download and install it, just run it on a terminal:
```bash
cargo install tuono
```
To check that it is correctly installed, run:
```bash
tuono --version
```
Run `tuono -h` to see all the available commands.
```bash
The React/Rust full-stack framework
Usage: tuono <COMMAND>
Commands:
dev Start the development environment
build Build the production assets
new Scaffold a new project
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
```
@@ -1,14 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - MDX"
canonical="https://tuono.dev/documentation/integrations/mdx"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Integrations' }]} />
# MDX
TODO
@@ -1,14 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Server side rendering"
canonical="https://tuono.dev/documentation/rendering/server-side-rendering"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Rendering' }]} />
# SSR
TODO
@@ -1,14 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Static site rendering"
canonical="https://tuono.dev/documentation/rendering/static-site-rendering"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Rendering' }]} />
# Static Site Rendering
TODO
@@ -1,101 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Routing"
canonical="https://tuono.dev/documentation/routing/defining-routes"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Defining routes' }]} />
# Defining routes
Tuono has a file-system based router built on the concept of routes.
When a file is added to the `src/routes` directory, it's automatically available as a route.
In Tuono, a page is a [React Component](https://react.dev/learn/your-first-component) exported from a `.tsx` file in the `src/routes` directory.
Each page is associated with a route based on its file name.
Every page is rendered server side by default.
Example:
If you create `src/routes/about.tsx` that exports a React component like:
```
export default function About() {
return <div>About</div>
}
```
The `About` component will be rendered when loading `/about` route.
## Index routes
The router will automatically route files named `index` to the root of the directory.
- `src/routes/index.tsx` → `/`
- `src/routes/blog/index.tsx`→ `/blog`
## Nested routes
You can also create a nested folder structure, and files will still be routed in the same way.
- `src/routes/blog/first-post.tsx` → `/blog/first-post`
- `src/routes/dashboard/settings/username.tsx` → `/dashboard/settings/username`
## Pages with Dynamic Routes
Tuono supports pages with dynamic routes segments.
For example, if you create a file called `src/routes/posts/[id].tsx`,
then it will be accessible at `posts/1`, `posts/2`, etc.
## The Root route (Layout components)
Tuono allows you to have a layout component to wrap all the routes included within the same folder.
To define such component you will have to create a `__root.tsx` file (is allowed only a single `__root` file per folder).
This file won't generate any route.
### Layout component for all routes
A file created in this location will wrap all project route.
```tsx
// src/routes/__root.tsx
export default function RootLayout({ children }) {
return <main>{children}</main>
}
```
### Layout component for specific routes
A file created in this location will wrap only the routes defined within the `src/routes/posts` folder.
```tsx
// src/routes/posts/__root.tsx
export default function PostRootLayout({ children }) {
return <article>{children}</article>
}
```
Referring to the two examples above consider that:
- `/posts/examples-post` → will be wrapped by:
```tsx
<RootLayout>
<PostRootLayout>
<ExamplePost />
</PostRootLayout>
</RootLayout>
```
- `/about` → will be wrapped by:
```tsx
<RootLayout>
<About />
</RootLayout>
```
@@ -1,61 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Routing"
canonical="https://tuono.dev/documentation/routing/dynamic-routes"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Dynamic routes' }]} />
# Dynamic routes
> 🚧 Only available for SSR apps. This feature will be soon available also for static generated apps.
When the exact segment names are not known in advance and you need to generate routes from dynamic data,
you can use dynamic segments, which are populated at request time.
## Convention
A dynamic segment can be created by wrapping a
file or a folder name in square brackets: `[segmentName]`.
The dynamic segment can be used in folders as well as file names:
- `src/routes/blog/[slug].tsx`
- `src/routes/blog/[slug]/index.tsx`
A path can contain multiple dynamic segments, allowing for more flexible routing:
- `src/routes/blog/[post]/[comment].tsx`
### Example
Assuming you're working on a blog with multiple posts,
you can use a dynamic segment to define the post page: `src/routes/blog/[slug].tsx`.
The `[slug]` is the dynamic segment which will be used to identify individual blog posts.
```tsx
import { useRouter } from 'tuono'
export default function Page() {
const router = useRouter()
return <p>Post: {router.pathname}</p>
}
```
## Catch all segments
Dynamic Segments can be extended to catch-all subsequent segments by adding
an ellipsis inside the brackets `[...segmentName]`.
Given the following `src/routes/shop/[...slug].tsx`,
the following paths will be matched:
- `/shop/clothes`
- `/shop/clothes/tops`
- `/shop`
- `/shop/clothes/tops/t-shirts`
- and so on.
@@ -1,14 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Routing"
canonical="https://tuono.dev/documentation/routing/intro"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Routing' }]} />
# Routing index
TODO
@@ -1,14 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Routing"
canonical="https://tuono.dev/documentation/routing/layouts"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Layouts' }]} />
# Layouts
Todo
@@ -1,75 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Routing"
canonical="https://tuono.dev/documentation/routing/link-and-navigation"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Link and navigation' }]} />
# Link and navigation
The Tuono router facilitates client-side route transitions between pages, enhancing the user experience by:
- Enabling smooth navigation in single-page applications (SPAs)
- Preventing page reloads during site navigation
How? Using the `Link` component provided from Tuono exports:
```tsx
import { Link } from 'tuono'
export default function HomePage() {
return (
<ul>
<li>
<Link href="/">Home</Link>
</li>
<li>
<Link href="/about">About Us</Link>
</li>
<li>
<Link href="/blog/hello-world">Blog Post</Link>
</li>
</ul>
)
}
```
The example above uses multiple links. Each one maps a path (`href`) to a known route:
- `/` → `src/routes/index.tsx`
- `/about` → `src/routes/about.tsx`
- `/blog/hello-world` → `src/routes/blog/[slug].tsx`
> Additional considerations:
>
> - Any `<Link />` in the viewport is prefetched by default when it appears within the viewport.
> - The corresponding data for server-rendered routes is fetched only when the `<Link />` is clicked.
## The `useRouter` hook
The Link component is not suitable for programmatic navigation.
To handle this, the library provides the `useRouter` hook.
For example, after a user submits a form and the API request succeeds, they can be redirected to another page.
```tsx
import { useRouter } from 'tuono'
export default function GoToAboutPage() {
const router = useRouter()
return (
<ProfileForm
onUpdateSuccess={() => {
router.push('/profile')
}}
/>
)
}
```
> For more information about this hook visit the [dedicated API reference page](/documentation/hooks/use-router).
@@ -1,14 +0,0 @@
import MetaTags from '@/components/meta-tags'
<MetaTags
title="Tuono - Routing"
canonical="https://tuono.dev/documentation/routing/loading-state"
/>
import Breadcrumbs, { Element } from '@/components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: 'Loading state' }]} />
# Loading state
Todo

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