mirror of
https://github.com/tuono-labs/tuono
synced 2026-07-25 12:52:47 -07:00
Add support for MDX (#17)
* feat: add support for MDX * fix: prevent empty server handler error message * doc: add MDX mention to README * feat: update version to v0.7.0
This commit is contained in:
+1
-1
@@ -6,7 +6,7 @@ members = [
|
||||
"crates/tuono_lib_macros",
|
||||
]
|
||||
exclude = [
|
||||
"examples/playground",
|
||||
"examples/mdx",
|
||||
"examples/tuono",
|
||||
"examples/tutorial"
|
||||
]
|
||||
|
||||
@@ -38,6 +38,7 @@ by Tuono based on the files defined within the `./src/routes` directory.
|
||||
- 🍭 CSS modules
|
||||
- 🧬 Server Side Rendering
|
||||
- 🧵 Multi thread backend
|
||||
- ⌨️ MDX support
|
||||
- ⚙️ Build optimizations
|
||||
- Custom APIs*
|
||||
- Image optimization*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tuono"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
edition = "2021"
|
||||
authors = ["V. Ageno <valerioageno@yahoo.it>"]
|
||||
description = "The react/rust fullstack framework"
|
||||
|
||||
@@ -33,7 +33,8 @@ pub const AXUM_ENTRY_POINT: &str = r##"
|
||||
// File automatically generated
|
||||
// Do not manually change it
|
||||
|
||||
use tuono_lib::{tokio, Mode, Server, axum::Router, axum::routing::get};
|
||||
use tuono_lib::{tokio, Mode, Server, axum::Router};
|
||||
// AXUM_GET_ROUTE_HANDLER
|
||||
|
||||
const MODE: Mode = /*MODE*/;
|
||||
|
||||
@@ -116,17 +117,32 @@ pub fn bundle_axum_source(mode: Mode) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_axum_source(source_builder: &App, mode: Mode) -> String {
|
||||
AXUM_ENTRY_POINT
|
||||
fn generate_axum_source(app: &App, mode: Mode) -> String {
|
||||
let src = AXUM_ENTRY_POINT
|
||||
.replace(
|
||||
"// ROUTE_BUILDER\n",
|
||||
&create_routes_declaration(&source_builder.route_map),
|
||||
&create_routes_declaration(&app.route_map),
|
||||
)
|
||||
.replace(
|
||||
"// MODULE_IMPORTS\n",
|
||||
&create_modules_declaration(&source_builder.route_map),
|
||||
&create_modules_declaration(&app.route_map),
|
||||
)
|
||||
.replace("/*MODE*/", mode.as_str())
|
||||
.replace("/*MODE*/", mode.as_str());
|
||||
|
||||
let has_server_handlers = app
|
||||
.route_map
|
||||
.iter()
|
||||
.filter(|(_, route)| route.axum_info.is_some())
|
||||
.collect::<Vec<(&String, &Route)>>()
|
||||
.is_empty();
|
||||
|
||||
if !has_server_handlers {
|
||||
return src.replace(
|
||||
"// AXUM_GET_ROUTE_HANDLER",
|
||||
"use tuono_lib::axum::routing::get;",
|
||||
);
|
||||
}
|
||||
src
|
||||
}
|
||||
|
||||
pub fn check_tuono_folder() -> io::Result<()> {
|
||||
@@ -166,4 +182,27 @@ mod tests {
|
||||
|
||||
assert!(prod_bundle.contains("const MODE: Mode = Mode::Prod;"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_load_the_axum_get_function() {
|
||||
let source_builder = App::new();
|
||||
|
||||
let dev_bundle = generate_axum_source(&source_builder, Mode::Dev);
|
||||
assert!(!dev_bundle.contains("use tuono_lib::axum::routing::get;"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_load_the_axum_get_function() {
|
||||
let mut source_builder = App::new();
|
||||
|
||||
let mut route = Route::new(String::from("index.tsx"));
|
||||
route.update_axum_info();
|
||||
|
||||
source_builder
|
||||
.route_map
|
||||
.insert(String::from("index.rs"), route);
|
||||
|
||||
let dev_bundle = generate_axum_source(&source_builder, Mode::Dev);
|
||||
assert!(dev_bundle.contains("use tuono_lib::axum::routing::get;"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tuono_lib"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
edition = "2021"
|
||||
authors = ["V. Ageno <valerioageno@yahoo.it>"]
|
||||
description = "The react/rust fullstack framework"
|
||||
@@ -32,7 +32,7 @@ regex = "1.10.5"
|
||||
either = "1.13.0"
|
||||
tower-http = {version = "0.5.2", features = ["fs"]}
|
||||
|
||||
tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.6.0"}
|
||||
tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.7.0"}
|
||||
# Match the same version used by axum
|
||||
tokio-tungstenite = "0.21.0"
|
||||
futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tuono_lib_macros"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
edition = "2021"
|
||||
description = "The react/rust fullstack framework"
|
||||
keywords = [ "react", "typescript", "fullstack", "web", "ssr"]
|
||||
|
||||
@@ -16,4 +16,13 @@ $ tuono new [NAME] --template [TEMPLATE]
|
||||
|
||||
`[TEMPLATE]` is the folder name.
|
||||
|
||||
## Troubleshooting for contributors
|
||||
|
||||
There is a common error that happens during example development due to
|
||||
pnpm workspace about the findings of different react versions.
|
||||
|
||||
To overcome this issue run within the single example folder:
|
||||
|
||||
```
|
||||
pnpm install --ignore-workspace && pnpm upgrade --save
|
||||
```
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.tuono
|
||||
out
|
||||
target
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "tuono"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "tuono"
|
||||
path = ".tuono/main.rs"
|
||||
|
||||
[dependencies]
|
||||
tuono_lib = { path = "../../crates/tuono_lib/"}
|
||||
serde = { version = "1.0.202", features = ["derive"] }
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Tuono starter
|
||||
|
||||
This is the starter tuono project. To download it run in your terminal:
|
||||
|
||||
```shell
|
||||
$ tuono new [NAME]
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "tuono",
|
||||
"description": "The react/rust fullstack framework",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@mdx-js/react": "^3.0.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"tuono": "link:../../packages/tuono"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"typescript": "^5.5.3"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
@@ -0,0 +1,14 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { MDXProvider } from '@mdx-js/react'
|
||||
|
||||
interface RootRouteProps {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export default function RootRoute({ children }: RootRouteProps): JSX.Element {
|
||||
return (
|
||||
<main className="main">
|
||||
<MDXProvider components={{}}>{children}</MDXProvider>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
# Index page
|
||||
@@ -0,0 +1,109 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
|
||||
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #fbfbfb;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 673px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.header a {
|
||||
color: black;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.title-wrap {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.title {
|
||||
position: absolute;
|
||||
font-size: 200px;
|
||||
line-height: 200px;
|
||||
z-index: 0;
|
||||
letter-spacing: -2px;
|
||||
margin-left: -8px;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.title span {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 140px;
|
||||
height: 30px;
|
||||
border: solid 3px black;
|
||||
border-radius: 10px;
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
color: white;
|
||||
background: black;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-left: 240px;
|
||||
position: relative;
|
||||
top: 25px;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.rust {
|
||||
animation: rotate 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.react {
|
||||
top: 33px;
|
||||
left: 28px;
|
||||
animation: rotate 6s linear infinite reverse;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 30px;
|
||||
line-height: 30px;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
|
||||
.subtitle-wrap {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
+2
-2
@@ -8,8 +8,8 @@
|
||||
"format": "turbo format --filter tuono",
|
||||
"format:check": "turbo format:check --filter tuono",
|
||||
"types": "turbo types --filter tuono",
|
||||
"test": "turbo test --filter tuono",
|
||||
"test:watch": "turbo test:watch --filter tuono"
|
||||
"test": "turbo test",
|
||||
"test:watch": "turbo test:watch"
|
||||
},
|
||||
"workspaces": [
|
||||
"tuono",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tuono-fs-router-vite-plugin",
|
||||
"version": "0.6.0",
|
||||
"version": "0.7.0",
|
||||
"description": "Plugin for the tuono's file system router. Tuono is the react/rust fullstack framework",
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
|
||||
@@ -46,7 +46,7 @@ async function getRouteNodes(
|
||||
|
||||
if (dirent.isDirectory()) {
|
||||
await recurse(relativePath)
|
||||
} else if (fullPath.match(/\.(tsx|ts|jsx|js)$/)) {
|
||||
} else if (fullPath.match(/\.(tsx|ts|jsx|js|mdx)$/)) {
|
||||
const filePath = replaceBackslash(path.join(dir, dirent.name))
|
||||
const filePathNoExt = removeExt(filePath)
|
||||
let routePath =
|
||||
@@ -220,6 +220,7 @@ export async function routeGenerator(config = defaultConfig): Promise<void> {
|
||||
|
||||
const imports = [
|
||||
...sortedRouteNodes.map((node) => {
|
||||
const extension = node.filePath.endsWith('mdx') ? '.mdx' : ''
|
||||
return `const ${
|
||||
node.variableName
|
||||
}Import = dynamic(() => import('./${replaceBackslash(
|
||||
@@ -230,7 +231,7 @@ export async function routeGenerator(config = defaultConfig): Promise<void> {
|
||||
),
|
||||
false,
|
||||
),
|
||||
)}'))`
|
||||
)}${extension}'))`
|
||||
}),
|
||||
].join('\n')
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// This file is auto-generated by Tuono
|
||||
|
||||
import { createRoute, dynamic } from 'tuono'
|
||||
|
||||
import RootImport from './routes/__root'
|
||||
|
||||
const AboutImport = dynamic(() => import('./routes/about.mdx'))
|
||||
const IndexImport = dynamic(() => import('./routes/index'))
|
||||
|
||||
const rootRoute = createRoute({ isRoot: true, component: RootImport })
|
||||
|
||||
const About = createRoute({ component: AboutImport })
|
||||
const Index = createRoute({ component: IndexImport })
|
||||
|
||||
// Create/Update Routes
|
||||
|
||||
const AboutRoute = About.update({
|
||||
path: '/about',
|
||||
getParentRoute: () => rootRoute,
|
||||
})
|
||||
|
||||
const IndexRoute = Index.update({
|
||||
path: '/',
|
||||
getParentRoute: () => rootRoute,
|
||||
})
|
||||
|
||||
// Create and export the route tree
|
||||
|
||||
export const routeTree = rootRoute.addChildren([IndexRoute, AboutRoute])
|
||||
@@ -0,0 +1 @@
|
||||
/** */
|
||||
@@ -0,0 +1 @@
|
||||
/** */
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tuono-lazy-fn-vite-plugin",
|
||||
"version": "0.6.0",
|
||||
"version": "0.7.0",
|
||||
"description": "Plugin for the tuono's lazy fn. Tuono is the react/rust fullstack framework",
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tuono",
|
||||
"version": "0.6.0",
|
||||
"version": "0.7.0",
|
||||
"description": "The react/rust fullstack framework",
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
@@ -84,6 +84,7 @@
|
||||
"@babel/template": "^7.24.0",
|
||||
"@babel/traverse": "^7.24.1",
|
||||
"@babel/types": "^7.24.0",
|
||||
"@mdx-js/rollup": "^3.0.1",
|
||||
"@types/babel__core": "^7.20.5",
|
||||
"@types/node": "^20.12.7",
|
||||
"@vitejs/plugin-react-swc": "^3.7.0",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { build, createServer, InlineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react-swc'
|
||||
import ViteFsRouter from 'tuono-fs-router-vite-plugin'
|
||||
import { LazyLoadingPlugin } from 'tuono-lazy-fn-vite-plugin'
|
||||
import mdx from '@mdx-js/rollup'
|
||||
|
||||
const BASE_CONFIG: InlineConfig = {
|
||||
root: '.tuono',
|
||||
@@ -9,7 +10,12 @@ const BASE_CONFIG: InlineConfig = {
|
||||
publicDir: '../public',
|
||||
cacheDir: 'cache',
|
||||
envDir: '../',
|
||||
plugins: [react(), ViteFsRouter(), LazyLoadingPlugin()],
|
||||
plugins: [
|
||||
{ enforce: 'pre', ...mdx() },
|
||||
react(),
|
||||
ViteFsRouter(),
|
||||
LazyLoadingPlugin(),
|
||||
],
|
||||
}
|
||||
|
||||
const VITE_PORT = 3001
|
||||
|
||||
Reference in New Issue
Block a user