Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions examples/with-koa/components/Edit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Edit({ name }: { name: string }) {
return (
<p className="py-4">
Edit <code>pages/{name}.tsx</code> and save to test HMR
</p>
)
}
32 changes: 32 additions & 0 deletions examples/with-koa/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"type": "module",
"name": "reactus-with-koa",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "tsx scripts/develop.ts",
"build": "tsx scripts/build.ts",
"start": "tsx scripts/start.ts"
},
"dependencies": {
"koa": "^2.15.0",
"@koa/router": "^12.0.1",
"koa-static": "^5.0.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"reactus": "0.2.10"
},
"devDependencies": {
"@types/koa": "^2.15.0",
"@types/koa__router": "^12.0.4",
"@types/koa-static": "^4.0.4",
"@types/node": "22.9.3",
"@types/react": "19.0.10",
"@types/react-dom": "19.0.4",
"@vitejs/plugin-react": "4.3.4",
"ts-node": "10.9.2",
"tsx": "4.19.3",
"typescript": "5.7.2",
"vite": "6.1.1"
}
}
28 changes: 28 additions & 0 deletions examples/with-koa/pages/about.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import './page.css';
import Edit from '../components/Edit';

export function Head({ styles = [] }: { styles?: string[] }) {
return (
<>
<title>About Reactus</title>
<meta name="description" content="About Reactus" />
<link rel="icon" type="image/svg+xml" href="/react.svg" />
<link rel="stylesheet" type="text/css" href="/global.css" />
{styles.map((href, index) => (
<link key={index} rel="stylesheet" type="text/css" href={href} />
))}
</>
)
}

export default function AboutPage() {
return (
<>
<h1 className="uppercase">About Reactus</h1>
<div className="p-4">
<Edit name="about" />
<a href="/">Home Page</a>
</div>
</>
)
}
34 changes: 34 additions & 0 deletions examples/with-koa/pages/home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import './page.css';
import { useState } from 'react';
import Edit from '../components/Edit';

export function Head({ styles = [] }: { styles?: string[] }) {
return (
<>
<title>Reactus</title>
<meta name="description" content="Reactus" />
<link rel="icon" type="image/svg+xml" href="/react.svg" />
<link rel="stylesheet" type="text/css" href="/global.css" />
{styles.map((href, index) => (
<link key={index} rel="stylesheet" type="text/css" href={href} />
))}
</>
)
}

export default function HomePage() {
const [count, setCount] = useState(0)

return (
<>
<h1>React + Reactus + Koa</h1>
<div className="p-4">
<button className="text-reactus" onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<Edit name="about" />
<a href="/about">About Reactus</a>
</div>
</>
)
}
7 changes: 7 additions & 0 deletions examples/with-koa/pages/page.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
:root { display: initial; }
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
70 changes: 70 additions & 0 deletions examples/with-koa/public/global.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
:root {
/*display: none; */
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;

color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;

font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}

a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}

body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}

h1 {
font-size: 3.2em;
line-height: 1.1;
}

button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}

@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
1 change: 1 addition & 0 deletions examples/with-koa/public/react.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions examples/with-koa/scripts/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//node
import path from 'node:path';
//reactus
import { build } from 'reactus';

async function builder() {
const cwd = process.cwd();
const engine = build({
cwd,
//path where to save assets (css, images, etc)
assetPath: path.join(cwd, 'public/assets'),
//path where to save and load (live) the client scripts (js)
clientPath: path.join(cwd, 'public/client'),
//path where to save and load (live) the server script (js)
pagePath: path.join(cwd, '.build/pages')
});

await engine.set('@/pages/home');
await engine.set('@/pages/about');

const responses = [
...await engine.buildAllClients(),
...await engine.buildAllAssets(),
...await engine.buildAllPages()
].map(response => {
const results = response.results;
if (typeof results?.contents === 'string') {
results.contents = results.contents.substring(0, 100) + ' ...';
}
return results;
});

//console.log(responses);
//fix for unused variable :)
if (responses.length) return;
}

builder().catch(e => {
console.error(e);
process.exit(1);
});
54 changes: 54 additions & 0 deletions examples/with-koa/scripts/develop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { dev } from 'reactus';
import Koa from 'koa';
import Router from '@koa/router';


async function develop() {
const engine = dev({
cwd: process.cwd(),
basePath: '/',
clientRoute: '/client',
})

const app = new Koa();
const router = new Router();

// Handle reactus http requests (public, assets, and hmr)
app.use(async (ctx, next) => {
await engine.http(ctx.req, ctx.res);

if (ctx.res.headersSent) return;
await next();
});

router.get('/', async (ctx) => {
ctx.set('Content-Type', 'text/html');
ctx.body = await engine.render('@/pages/home', { title: 'Home' });
return;
});

router.get('/about', async (ctx) => {
ctx.set('Content-Type', 'text/html');
ctx.body = await engine.render('@/pages/about');
return;
});

app.use(router.routes()).use(router.allowedMethods());

// Catch-all middleware for 404
app.use(async (ctx) => {
if (!ctx.body) {
ctx.status = 404;
ctx.body = '404 not found.';
}
});

app.listen(3000, () => {
console.log('Server listening at http://localhost:3000');
});
}

develop().catch(e => {
console.error(e);
process.exit(1);
});
68 changes: 68 additions & 0 deletions examples/with-koa/scripts/start.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import path from 'node:path';
import { serve } from 'reactus';
import Koa from 'koa';
import Router from '@koa/router';
import koaStatic from 'koa-static';

async function start() {
const app = new Koa();
const router = new Router();
const cwd = process.cwd();

const engine = serve({
cwd,
clientRoute: '/client',
pagePath: path.join(cwd, '.build/pages'),
cssRoute: '/assets'
});

const assets = koaStatic(path.join(cwd, 'public'), {
maxage: 31536000,
immutable: true,
});

// Middleware using koa-static to handle public assets
app.use(assets);

router.get('/', async (ctx) => {
try {
ctx.set('Content-Type', 'text/html');
ctx.body = await engine.render('@/pages/home');
} catch (error) {
console.error('Error rendering /home:', error);
ctx.status = 500;
ctx.body = 'Internal Server Error';
}
});

router.get('/about', async (ctx) => {
try {
ctx.set('Content-Type', 'text/html');
ctx.body = await engine.render('@/pages/about');
} catch (error) {
console.error('Error rendering /about:', error);
ctx.status = 500;
ctx.body = 'Internal Server Error';
}
});

app.use(router.routes()).use(router.allowedMethods());

// Catch-all middleware for 404
app.use(async (ctx) => {
if (!ctx.body) {
ctx.status = 404;
ctx.body = 'Not Found.';
}
});


app.listen(3000,() => {
console.log('Server listening at http://localhost:3000');
});
}

start().catch(e => {
console.error(e);
process.exit(1);
});
13 changes: 13 additions & 0 deletions examples/with-koa/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "reactus/tsconfig/client",
"compilerOptions": {
"moduleResolution": "bundler",
"outDir": ".build",
},
"include": [
"scripts/**/*.ts",
"components/**/*.tsx" ,
"pages/**/*.tsx"
],
"exclude": [ "dist", "node_modules", "tests" ]
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"fastify": "yarn --cwd examples/with-fastify",
"hapi": "yarn --cwd examples/with-hapi",
"http": "yarn --cwd examples/with-http",
"koa": "yarn --cwd examples/with-koa",
"nest": "yarn --cwd examples/with-nest",
"restify": "yarn --cwd examples/with-restify",
"tailwind": "yarn --cwd examples/with-tailwind",
Expand Down