@evjs/cli 0.0.1-rc.18 → 0.0.1-rc.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -6
- package/dist/index.js +1 -88
- package/package.json +2 -5
- package/templates/basic-csr/README.md +0 -23
- package/templates/basic-csr/index.html +0 -11
- package/templates/basic-csr/package.json +0 -21
- package/templates/basic-csr/src/main.tsx +0 -22
- package/templates/basic-csr/src/pages/__root.tsx +0 -28
- package/templates/basic-csr/src/pages/about.tsx +0 -19
- package/templates/basic-csr/src/pages/home.tsx +0 -19
- package/templates/basic-csr/src/pages/posts/index.tsx +0 -63
- package/templates/basic-csr/tsconfig.json +0 -17
- package/templates/basic-server-fns/README.md +0 -24
- package/templates/basic-server-fns/index.html +0 -11
- package/templates/basic-server-fns/package.json +0 -21
- package/templates/basic-server-fns/src/api/users.server.ts +0 -31
- package/templates/basic-server-fns/src/main.tsx +0 -12
- package/templates/basic-server-fns/src/routes.tsx +0 -108
- package/templates/basic-server-fns/tsconfig.json +0 -16
- package/templates/complex-routing/README.md +0 -27
- package/templates/complex-routing/index.html +0 -11
- package/templates/complex-routing/package.json +0 -21
- package/templates/complex-routing/src/api/data.server.ts +0 -86
- package/templates/complex-routing/src/main.tsx +0 -29
- package/templates/complex-routing/src/pages/__root.tsx +0 -60
- package/templates/complex-routing/src/pages/catch.tsx +0 -26
- package/templates/complex-routing/src/pages/dashboard.tsx +0 -81
- package/templates/complex-routing/src/pages/home.tsx +0 -35
- package/templates/complex-routing/src/pages/posts/index.tsx +0 -104
- package/templates/complex-routing/src/pages/search.tsx +0 -71
- package/templates/complex-routing/src/pages/user.tsx +0 -32
- package/templates/complex-routing/tsconfig.json +0 -13
- package/templates/configured-server-fns/README.md +0 -24
- package/templates/configured-server-fns/ev.config.ts +0 -56
- package/templates/configured-server-fns/index.html +0 -11
- package/templates/configured-server-fns/package.json +0 -21
- package/templates/configured-server-fns/src/api/users.server.ts +0 -22
- package/templates/configured-server-fns/src/main.tsx +0 -12
- package/templates/configured-server-fns/src/routes.tsx +0 -111
- package/templates/configured-server-fns/tsconfig.json +0 -16
- package/templates/with-tailwind/ev.config.ts +0 -20
- package/templates/with-tailwind/index.html +0 -11
- package/templates/with-tailwind/package.json +0 -28
- package/templates/with-tailwind/postcss.config.mjs +0 -5
- package/templates/with-tailwind/src/main.tsx +0 -15
- package/templates/with-tailwind/src/pages/__root.tsx +0 -30
- package/templates/with-tailwind/src/pages/home.tsx +0 -85
- package/templates/with-tailwind/src/styles.css +0 -2
- package/templates/with-tailwind/tsconfig.json +0 -17
package/README.md
CHANGED
|
@@ -22,14 +22,10 @@ No configuration file is needed. `ev dev` and `ev build` work out of the box wit
|
|
|
22
22
|
|
|
23
23
|
| Command | Description |
|
|
24
24
|
|---------|-------------|
|
|
25
|
-
| `ev init [name]` | Scaffold a new project from a template |
|
|
26
25
|
| `ev dev` | Start dev server (client HMR + API watch) |
|
|
27
26
|
| `ev build` | Production build (client + server) |
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Templates: `basic-csr`, `basic-server-fns`, `configured-server-fns`, `complex-routing`, `with-tailwind`.
|
|
32
|
-
Option: `-t, --template <template>` to skip interactive selection.
|
|
28
|
+
> **Scaffolding:** Use `npx @evjs/create-app` to scaffold a new project.
|
|
33
29
|
|
|
34
30
|
### `ev dev`
|
|
35
31
|
|
|
@@ -90,7 +86,7 @@ my-app/
|
|
|
90
86
|
1. **Don't create `webpack.config.cjs`** — use `ev.config.ts` instead
|
|
91
87
|
2. **Don't install webpack manually** — it's a dependency of `@evjs/cli`
|
|
92
88
|
3. **Config file must be `ev.config.ts`** — not `evjs.config.ts`
|
|
93
|
-
4. **Import `defineConfig` from `@evjs/cli`** — not from `@evjs/
|
|
89
|
+
4. **Import `defineConfig` from `@evjs/cli`** — not from `@evjs/server`
|
|
94
90
|
|
|
95
91
|
## Bundled Dependencies
|
|
96
92
|
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,6 @@ import { configure, getConsoleSink, getLogger } from "@logtape/logtape";
|
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import { execa } from "execa";
|
|
8
8
|
import fs from "fs-extra";
|
|
9
|
-
import prompts from "prompts";
|
|
10
9
|
import { CONFIG_DEFAULTS } from "./config.js";
|
|
11
10
|
const esmRequire = createRequire(import.meta.url);
|
|
12
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -24,92 +23,6 @@ program
|
|
|
24
23
|
.name("ev")
|
|
25
24
|
.description("CLI for the evjs framework")
|
|
26
25
|
.version(pkg.version);
|
|
27
|
-
program
|
|
28
|
-
.command("init")
|
|
29
|
-
.description("Initialize a new evjs project")
|
|
30
|
-
.argument("[name]", "Project name")
|
|
31
|
-
.option("-t, --template <template>", "Template to use")
|
|
32
|
-
.action(async (name, options) => {
|
|
33
|
-
const response = await prompts([
|
|
34
|
-
{
|
|
35
|
-
type: name ? null : "text",
|
|
36
|
-
name: "projectName",
|
|
37
|
-
message: "Project name:",
|
|
38
|
-
initial: name || "my-evjs-app",
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
type: options.template ? null : "select",
|
|
42
|
-
name: "template",
|
|
43
|
-
message: "Select a template:",
|
|
44
|
-
choices: [
|
|
45
|
-
{ title: "Basic CSR (Client-Side Rendering)", value: "basic-csr" },
|
|
46
|
-
{ title: "Basic Server Functions", value: "basic-server-fns" },
|
|
47
|
-
{
|
|
48
|
-
title: "Configured Server Functions (ev.config.ts + Query)",
|
|
49
|
-
value: "configured-server-fns",
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
title: "Complex Routing (params, search, layouts, loaders)",
|
|
53
|
-
value: "complex-routing",
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
title: "With Tailwind CSS (plugin loaders example)",
|
|
57
|
-
value: "with-tailwind",
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
},
|
|
61
|
-
], {
|
|
62
|
-
onCancel: () => {
|
|
63
|
-
process.exit(1);
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
const projectName = response.projectName || name;
|
|
67
|
-
const template = response.template || options.template;
|
|
68
|
-
const targetDir = path.resolve(process.cwd(), projectName);
|
|
69
|
-
if (fs.existsSync(targetDir)) {
|
|
70
|
-
logger.error `Directory ${projectName} already exists!`;
|
|
71
|
-
process.exit(1);
|
|
72
|
-
}
|
|
73
|
-
const templateDir = path.resolve(__dirname, "../templates", template);
|
|
74
|
-
if (!fs.existsSync(templateDir)) {
|
|
75
|
-
logger.error `Template ${template} not found!`;
|
|
76
|
-
process.exit(1);
|
|
77
|
-
}
|
|
78
|
-
logger.info `Scaffolding project in ${targetDir}...`;
|
|
79
|
-
await fs.copy(templateDir, targetDir, {
|
|
80
|
-
dereference: true,
|
|
81
|
-
filter: (src) => {
|
|
82
|
-
const basename = path.basename(src);
|
|
83
|
-
return !["node_modules", "dist", ".turbo"].includes(basename);
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
// Post-process package.json: sync @evjs/* versions and set project name
|
|
87
|
-
const pkgPath = path.join(targetDir, "package.json");
|
|
88
|
-
if (fs.existsSync(pkgPath)) {
|
|
89
|
-
const pkg = await fs.readJson(pkgPath);
|
|
90
|
-
pkg.name = projectName;
|
|
91
|
-
delete pkg.private; // Templates shouldn't be private by default
|
|
92
|
-
const updateDeps = (deps) => {
|
|
93
|
-
if (!deps)
|
|
94
|
-
return;
|
|
95
|
-
for (const [name, val] of Object.entries(deps)) {
|
|
96
|
-
// Sync all @evjs/* packages to current CLI version
|
|
97
|
-
if (name.startsWith("@evjs/") &&
|
|
98
|
-
(val === "*" ||
|
|
99
|
-
(typeof val === "string" && val.includes("workspace")))) {
|
|
100
|
-
deps[name] = `^${pkg.version}`;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
updateDeps(pkg.dependencies);
|
|
105
|
-
updateDeps(pkg.devDependencies);
|
|
106
|
-
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
107
|
-
}
|
|
108
|
-
logger.info `Done! Now run:`;
|
|
109
|
-
logger.info ` cd ${projectName}`;
|
|
110
|
-
logger.info ` npm install`;
|
|
111
|
-
logger.info ` npm run dev`;
|
|
112
|
-
});
|
|
113
26
|
/**
|
|
114
27
|
* Load config and create webpack configuration object.
|
|
115
28
|
*
|
|
@@ -161,7 +74,7 @@ program
|
|
|
161
74
|
fs.writeFileSync(bootstrapPath, [
|
|
162
75
|
`const bundle = require(${JSON.stringify(serverBundlePath)});`,
|
|
163
76
|
`const app = bundle.createApp({ endpoint: ${JSON.stringify(evjsConfig?.server?.endpoint ?? CONFIG_DEFAULTS.endpoint)} });`,
|
|
164
|
-
`const { serve } = require("@evjs/
|
|
77
|
+
`const { serve } = require("@evjs/server/node");`,
|
|
165
78
|
`serve(app, { port: ${serverPort} });`,
|
|
166
79
|
].join("\n"));
|
|
167
80
|
// node gets --watch flags; other runtimes use their own args as-is
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evjs/cli",
|
|
3
|
-
"version": "0.0.1-rc.
|
|
3
|
+
"version": "0.0.1-rc.27",
|
|
4
4
|
"description": "CLI and configuration layer for the evjs framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/config.js",
|
|
@@ -18,8 +18,7 @@
|
|
|
18
18
|
"ev": "dist/index.js"
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
|
-
"dist"
|
|
22
|
-
"templates"
|
|
21
|
+
"dist"
|
|
23
22
|
],
|
|
24
23
|
"scripts": {
|
|
25
24
|
"build": "tsc",
|
|
@@ -38,7 +37,6 @@
|
|
|
38
37
|
"glob": "^13.0.6",
|
|
39
38
|
"html-webpack-plugin": "^5.6.6",
|
|
40
39
|
"picocolors": "^1.1.1",
|
|
41
|
-
"prompts": "^2.4.2",
|
|
42
40
|
"swc-loader": "^0.2.7",
|
|
43
41
|
"webpack": "^5.105.4",
|
|
44
42
|
"webpack-cli": "^6.0.1",
|
|
@@ -47,7 +45,6 @@
|
|
|
47
45
|
"devDependencies": {
|
|
48
46
|
"@types/fs-extra": "^11.0.4",
|
|
49
47
|
"@types/node": "^22.13.5",
|
|
50
|
-
"@types/prompts": "^2.4.9",
|
|
51
48
|
"typescript": "^5.7.3"
|
|
52
49
|
},
|
|
53
50
|
"homepage": "https://github.com/evaijs/evjs#readme",
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# basic-csr
|
|
2
|
-
|
|
3
|
-
Minimal client-side rendering example — no server functions, just routing.
|
|
4
|
-
|
|
5
|
-
## Run
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm run dev -w example-basic-csr
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Key Files
|
|
12
|
-
|
|
13
|
-
| File | Purpose |
|
|
14
|
-
|------|---------|
|
|
15
|
-
| `src/main.tsx` | App bootstrap with `createApp` |
|
|
16
|
-
| `src/pages/` | Route components (Home, Posts) |
|
|
17
|
-
|
|
18
|
-
## What It Demonstrates
|
|
19
|
-
|
|
20
|
-
- `createApp` + `createRoute` from `@evjs/runtime`
|
|
21
|
-
- Client-side routing with TanStack Router
|
|
22
|
-
- Dynamic route params (`$postId`)
|
|
23
|
-
- No server functions — pure CSR
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "example-basic-csr",
|
|
3
|
-
"version": "0.0.1-rc.18",
|
|
4
|
-
"private": true,
|
|
5
|
-
"scripts": {
|
|
6
|
-
"dev": "ev dev",
|
|
7
|
-
"build": "ev build",
|
|
8
|
-
"check-types": "tsc --noEmit"
|
|
9
|
-
},
|
|
10
|
-
"dependencies": {
|
|
11
|
-
"@evjs/runtime": "*",
|
|
12
|
-
"react": "^19.2.4",
|
|
13
|
-
"react-dom": "^19.2.4"
|
|
14
|
-
},
|
|
15
|
-
"devDependencies": {
|
|
16
|
-
"@evjs/cli": "*",
|
|
17
|
-
"@types/react": "^19.0.8",
|
|
18
|
-
"@types/react-dom": "^19.0.3",
|
|
19
|
-
"typescript": "^5.7.3"
|
|
20
|
-
}
|
|
21
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { createApp } from "@evjs/runtime/client";
|
|
2
|
-
import { rootRoute } from "./pages/__root";
|
|
3
|
-
import { aboutRoute } from "./pages/about";
|
|
4
|
-
import { homeRoute } from "./pages/home";
|
|
5
|
-
import { postDetailRoute, postsIndexRoute, postsRoute } from "./pages/posts";
|
|
6
|
-
|
|
7
|
-
const routeTree = rootRoute.addChildren([
|
|
8
|
-
homeRoute,
|
|
9
|
-
aboutRoute,
|
|
10
|
-
postsRoute.addChildren([postsIndexRoute, postDetailRoute]),
|
|
11
|
-
]);
|
|
12
|
-
|
|
13
|
-
const app = createApp({ routeTree });
|
|
14
|
-
|
|
15
|
-
// Register router type for full IDE type-safety on useParams, useSearch, Link, etc.
|
|
16
|
-
declare module "@tanstack/react-router" {
|
|
17
|
-
interface Register {
|
|
18
|
-
router: typeof app.router;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
app.render("#app");
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { createRootRoute, Link, Outlet } from "@evjs/runtime/client";
|
|
2
|
-
|
|
3
|
-
function Root() {
|
|
4
|
-
return (
|
|
5
|
-
<div style={{ fontFamily: "system-ui, sans-serif", padding: "1rem" }}>
|
|
6
|
-
<nav
|
|
7
|
-
style={{
|
|
8
|
-
display: "flex",
|
|
9
|
-
gap: "1rem",
|
|
10
|
-
borderBottom: "1px solid #e5e7eb",
|
|
11
|
-
paddingBottom: "0.5rem",
|
|
12
|
-
marginBottom: "1rem",
|
|
13
|
-
}}
|
|
14
|
-
>
|
|
15
|
-
<Link to="/" style={{ fontWeight: "bold" }}>
|
|
16
|
-
Home
|
|
17
|
-
</Link>
|
|
18
|
-
<Link to="/about">About</Link>
|
|
19
|
-
<Link to="/posts">Posts</Link>
|
|
20
|
-
</nav>
|
|
21
|
-
<Outlet />
|
|
22
|
-
</div>
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const rootRoute = createRootRoute({
|
|
27
|
-
component: Root,
|
|
28
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { createRoute } from "@evjs/runtime/client";
|
|
2
|
-
import { rootRoute } from "./__root";
|
|
3
|
-
|
|
4
|
-
function About() {
|
|
5
|
-
return (
|
|
6
|
-
<div>
|
|
7
|
-
<h1>About</h1>
|
|
8
|
-
<p>
|
|
9
|
-
Code-based routing with TanStack Router via <code>createApp</code>.
|
|
10
|
-
</p>
|
|
11
|
-
</div>
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const aboutRoute = createRoute({
|
|
16
|
-
getParentRoute: () => rootRoute,
|
|
17
|
-
path: "/about",
|
|
18
|
-
component: About,
|
|
19
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { createRoute } from "@evjs/runtime/client";
|
|
2
|
-
import { rootRoute } from "./__root";
|
|
3
|
-
|
|
4
|
-
function Home() {
|
|
5
|
-
return (
|
|
6
|
-
<div>
|
|
7
|
-
<h1>Welcome Home!</h1>
|
|
8
|
-
<p>
|
|
9
|
-
A basic client-side rendered app built with <code>@evjs/runtime</code>.
|
|
10
|
-
</p>
|
|
11
|
-
</div>
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const homeRoute = createRoute({
|
|
16
|
-
getParentRoute: () => rootRoute,
|
|
17
|
-
path: "/",
|
|
18
|
-
component: Home,
|
|
19
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { createRoute, Link, Outlet } from "@evjs/runtime/client";
|
|
2
|
-
import { rootRoute } from "../__root";
|
|
3
|
-
|
|
4
|
-
const posts = [
|
|
5
|
-
{ id: "1", title: "First Post", body: "Hello world!" },
|
|
6
|
-
{ id: "2", title: "Second Post", body: "Another day, another post." },
|
|
7
|
-
{ id: "3", title: "Third Post", body: "Three's a charm." },
|
|
8
|
-
];
|
|
9
|
-
|
|
10
|
-
function Posts() {
|
|
11
|
-
return (
|
|
12
|
-
<div style={{ display: "flex", gap: "1rem" }}>
|
|
13
|
-
<ul style={{ listStyle: "none", padding: 0, minWidth: 160 }}>
|
|
14
|
-
{posts.map((p) => (
|
|
15
|
-
<li key={p.id} style={{ marginBottom: "0.25rem" }}>
|
|
16
|
-
<Link
|
|
17
|
-
to="/posts/$postId"
|
|
18
|
-
params={{ postId: p.id }}
|
|
19
|
-
activeProps={{ style: { fontWeight: "bold" } }}
|
|
20
|
-
>
|
|
21
|
-
{p.title}
|
|
22
|
-
</Link>
|
|
23
|
-
</li>
|
|
24
|
-
))}
|
|
25
|
-
</ul>
|
|
26
|
-
<Outlet />
|
|
27
|
-
</div>
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const postsRoute = createRoute({
|
|
32
|
-
getParentRoute: () => rootRoute,
|
|
33
|
-
path: "/posts",
|
|
34
|
-
component: Posts,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
function PostsIndex() {
|
|
38
|
-
return <p style={{ color: "#6b7280" }}>Select a post.</p>;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export const postsIndexRoute = createRoute({
|
|
42
|
-
getParentRoute: () => postsRoute,
|
|
43
|
-
path: "/",
|
|
44
|
-
component: PostsIndex,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
function PostDetail() {
|
|
48
|
-
const { postId } = postDetailRoute.useParams();
|
|
49
|
-
const post = posts.find((p) => p.id === postId);
|
|
50
|
-
if (!post) return <p>Post not found.</p>;
|
|
51
|
-
return (
|
|
52
|
-
<div>
|
|
53
|
-
<h2>{post.title}</h2>
|
|
54
|
-
<p>{post.body}</p>
|
|
55
|
-
</div>
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export const postDetailRoute = createRoute({
|
|
60
|
-
getParentRoute: () => postsRoute,
|
|
61
|
-
path: "$postId",
|
|
62
|
-
component: PostDetail,
|
|
63
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ESNext",
|
|
4
|
-
"jsx": "react-jsx",
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
7
|
-
"moduleResolution": "Bundler",
|
|
8
|
-
"verbatimModuleSyntax": true,
|
|
9
|
-
"noEmit": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"strict": true,
|
|
12
|
-
"noUnusedLocals": true,
|
|
13
|
-
"noUnusedParameters": true,
|
|
14
|
-
"noFallthroughCasesInSwitch": true
|
|
15
|
-
},
|
|
16
|
-
"include": ["src"]
|
|
17
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# basic-server-fns
|
|
2
|
-
|
|
3
|
-
Basic server functions with `query()` / `mutation()` wrappers.
|
|
4
|
-
|
|
5
|
-
## Run
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm run dev -w example-basic-server-fns
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Key Files
|
|
12
|
-
|
|
13
|
-
| File | Purpose |
|
|
14
|
-
|------|---------|
|
|
15
|
-
| `src/main.tsx` | App bootstrap |
|
|
16
|
-
| `src/routes.tsx` | Routes + components |
|
|
17
|
-
| `src/api/users.server.ts` | `"use server"` CRUD functions |
|
|
18
|
-
|
|
19
|
-
## What It Demonstrates
|
|
20
|
-
|
|
21
|
-
- `"use server"` directive for auto-discovered server functions
|
|
22
|
-
- `query(fn).useQuery()` for data fetching
|
|
23
|
-
- `mutation(fn).useMutation()` for mutations
|
|
24
|
-
- `invalidates` for auto cache invalidation on mutation success
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "example-basic-server-fns",
|
|
3
|
-
"version": "0.0.1-rc.18",
|
|
4
|
-
"private": true,
|
|
5
|
-
"scripts": {
|
|
6
|
-
"dev": "ev dev",
|
|
7
|
-
"build": "ev build",
|
|
8
|
-
"check-types": "tsc --noEmit"
|
|
9
|
-
},
|
|
10
|
-
"dependencies": {
|
|
11
|
-
"@evjs/runtime": "*",
|
|
12
|
-
"react": "^19.2.4",
|
|
13
|
-
"react-dom": "^19.2.4"
|
|
14
|
-
},
|
|
15
|
-
"devDependencies": {
|
|
16
|
-
"@evjs/cli": "*",
|
|
17
|
-
"@types/react": "^19.1.8",
|
|
18
|
-
"@types/react-dom": "^19.1.8",
|
|
19
|
-
"typescript": "^5.7.3"
|
|
20
|
-
}
|
|
21
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"use server";
|
|
2
|
-
|
|
3
|
-
/** Simulated user database. */
|
|
4
|
-
const users = [
|
|
5
|
-
{ id: "1", name: "Alice", email: "alice@example.com" },
|
|
6
|
-
{ id: "2", name: "Bob", email: "bob@example.com" },
|
|
7
|
-
{ id: "3", name: "Charlie", email: "charlie@example.com" },
|
|
8
|
-
];
|
|
9
|
-
|
|
10
|
-
/** Get all users. */
|
|
11
|
-
export async function getUsers() {
|
|
12
|
-
// Simulate server latency
|
|
13
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
14
|
-
return users;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** Get a single user by ID. */
|
|
18
|
-
export async function getUser(id: string) {
|
|
19
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
20
|
-
const user = users.find((u) => u.id === id);
|
|
21
|
-
if (!user) throw new Error(`User ${id} not found`);
|
|
22
|
-
return user;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** Create a new user. */
|
|
26
|
-
export async function createUser(data: { name: string; email: string }) {
|
|
27
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
28
|
-
const newUser = { id: String(users.length + 1), ...data };
|
|
29
|
-
users.push(newUser);
|
|
30
|
-
return newUser;
|
|
31
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { createApp } from "@evjs/runtime/client";
|
|
2
|
-
import { routeTree } from "./routes";
|
|
3
|
-
|
|
4
|
-
const app = createApp({ routeTree });
|
|
5
|
-
|
|
6
|
-
declare module "@tanstack/react-router" {
|
|
7
|
-
interface Register {
|
|
8
|
-
router: typeof app.router;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
app.render("#app");
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createRootRoute,
|
|
3
|
-
createRoute,
|
|
4
|
-
Link,
|
|
5
|
-
Outlet,
|
|
6
|
-
} from "@evjs/runtime/client";
|
|
7
|
-
import { useEffect, useState } from "react";
|
|
8
|
-
import { createUser, getUsers } from "./api/users.server";
|
|
9
|
-
|
|
10
|
-
// ── Root Route ──
|
|
11
|
-
|
|
12
|
-
function Root() {
|
|
13
|
-
return (
|
|
14
|
-
<div style={{ fontFamily: "system-ui, sans-serif", padding: "1rem" }}>
|
|
15
|
-
<h1>Server Functions Example</h1>
|
|
16
|
-
<nav style={{ display: "flex", gap: "1rem", marginBottom: "1rem" }}>
|
|
17
|
-
<Link to="/">Users</Link>
|
|
18
|
-
</nav>
|
|
19
|
-
<Outlet />
|
|
20
|
-
</div>
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const rootRoute = createRootRoute({ component: Root });
|
|
25
|
-
|
|
26
|
-
// ── Users Route ──
|
|
27
|
-
|
|
28
|
-
function UsersPage() {
|
|
29
|
-
const [name, setName] = useState("");
|
|
30
|
-
const [email, setEmail] = useState("");
|
|
31
|
-
|
|
32
|
-
const [users, setUsers] = useState<
|
|
33
|
-
{ id: string; name: string; email: string }[]
|
|
34
|
-
>([]);
|
|
35
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
36
|
-
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
let mounted = true;
|
|
39
|
-
getUsers()
|
|
40
|
-
.then((data) => {
|
|
41
|
-
if (mounted) {
|
|
42
|
-
setUsers(data);
|
|
43
|
-
setIsLoading(false);
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
.catch((err) => {
|
|
47
|
-
console.error(err);
|
|
48
|
-
if (mounted) setIsLoading(false);
|
|
49
|
-
});
|
|
50
|
-
return () => {
|
|
51
|
-
mounted = false;
|
|
52
|
-
};
|
|
53
|
-
}, []);
|
|
54
|
-
|
|
55
|
-
async function handleCreate(e: { preventDefault: () => void }) {
|
|
56
|
-
e.preventDefault();
|
|
57
|
-
if (!name || !email) return;
|
|
58
|
-
try {
|
|
59
|
-
await createUser({ name, email });
|
|
60
|
-
setName("");
|
|
61
|
-
setEmail("");
|
|
62
|
-
const data = await getUsers();
|
|
63
|
-
setUsers(data);
|
|
64
|
-
} catch (err) {
|
|
65
|
-
console.error(err);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (isLoading) return <p>Loading users from server…</p>;
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<div>
|
|
73
|
-
<h2>Users (fetched via direct server function call)</h2>
|
|
74
|
-
<ul>
|
|
75
|
-
{users.map((u) => (
|
|
76
|
-
<li key={u.id}>
|
|
77
|
-
{u.name} — {u.email}
|
|
78
|
-
</li>
|
|
79
|
-
))}
|
|
80
|
-
</ul>
|
|
81
|
-
|
|
82
|
-
<h3>Add User</h3>
|
|
83
|
-
<form onSubmit={handleCreate} style={{ display: "flex", gap: "0.5rem" }}>
|
|
84
|
-
<input
|
|
85
|
-
placeholder="Name"
|
|
86
|
-
value={name}
|
|
87
|
-
onChange={(e) => setName(e.target.value)}
|
|
88
|
-
/>
|
|
89
|
-
<input
|
|
90
|
-
placeholder="Email"
|
|
91
|
-
value={email}
|
|
92
|
-
onChange={(e) => setEmail(e.target.value)}
|
|
93
|
-
/>
|
|
94
|
-
<button type="submit">Create</button>
|
|
95
|
-
</form>
|
|
96
|
-
</div>
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const usersRoute = createRoute({
|
|
101
|
-
getParentRoute: () => rootRoute,
|
|
102
|
-
path: "/",
|
|
103
|
-
component: UsersPage,
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// ── Route Tree ──
|
|
107
|
-
|
|
108
|
-
export const routeTree = rootRoute.addChildren([usersRoute]);
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"include": ["src"],
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"target": "ESNext",
|
|
5
|
-
"jsx": "react-jsx",
|
|
6
|
-
"module": "ESNext",
|
|
7
|
-
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
8
|
-
"moduleResolution": "Bundler",
|
|
9
|
-
"outDir": "./dist",
|
|
10
|
-
"rootDir": "./src",
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"strict": true,
|
|
13
|
-
"noEmit": true,
|
|
14
|
-
"verbatimModuleSyntax": true
|
|
15
|
-
}
|
|
16
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# complex-routing
|
|
2
|
-
|
|
3
|
-
Advanced routing patterns with TanStack Router.
|
|
4
|
-
|
|
5
|
-
## Run
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm run dev -w example-complex-routing
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Key Files
|
|
12
|
-
|
|
13
|
-
| File | Purpose |
|
|
14
|
-
|------|---------|
|
|
15
|
-
| `src/main.tsx` | Route tree assembly |
|
|
16
|
-
| `src/pages/__root.tsx` | Root layout with navigation |
|
|
17
|
-
| `src/pages/home.tsx` | Index route |
|
|
18
|
-
| `src/pages/posts/` | Nested routes with loader |
|
|
19
|
-
|
|
20
|
-
## What It Demonstrates
|
|
21
|
-
|
|
22
|
-
- Dynamic route params (`$postId`)
|
|
23
|
-
- Pathless layout routes
|
|
24
|
-
- Route loaders with `queryClient.ensureQueryData`
|
|
25
|
-
- Search params with `validateSearch`
|
|
26
|
-
- Catch-all 404 route
|
|
27
|
-
- Type-safe `route.useParams()`
|