@evjs/cli 0.0.1-rc.13

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.
Files changed (40) hide show
  1. package/AGENT.md +121 -0
  2. package/README.md +67 -0
  3. package/dist/config.js +35 -0
  4. package/dist/create-webpack-config.js +99 -0
  5. package/dist/index.js +220 -0
  6. package/dist/load-config.js +39 -0
  7. package/package.json +75 -0
  8. package/templates/basic-csr/index.html +11 -0
  9. package/templates/basic-csr/package.json +21 -0
  10. package/templates/basic-csr/src/main.tsx +22 -0
  11. package/templates/basic-csr/src/pages/__root.tsx +28 -0
  12. package/templates/basic-csr/src/pages/about.tsx +19 -0
  13. package/templates/basic-csr/src/pages/home.tsx +19 -0
  14. package/templates/basic-csr/src/pages/posts/index.tsx +63 -0
  15. package/templates/basic-csr/tsconfig.json +17 -0
  16. package/templates/basic-server-fns/index.html +11 -0
  17. package/templates/basic-server-fns/package.json +21 -0
  18. package/templates/basic-server-fns/src/api/users.server.ts +31 -0
  19. package/templates/basic-server-fns/src/main.tsx +12 -0
  20. package/templates/basic-server-fns/src/routes.tsx +108 -0
  21. package/templates/basic-server-fns/tsconfig.json +16 -0
  22. package/templates/complex-routing/index.html +11 -0
  23. package/templates/complex-routing/package.json +21 -0
  24. package/templates/complex-routing/src/api/data.server.ts +86 -0
  25. package/templates/complex-routing/src/main.tsx +29 -0
  26. package/templates/complex-routing/src/pages/__root.tsx +60 -0
  27. package/templates/complex-routing/src/pages/catch.tsx +26 -0
  28. package/templates/complex-routing/src/pages/dashboard.tsx +81 -0
  29. package/templates/complex-routing/src/pages/home.tsx +35 -0
  30. package/templates/complex-routing/src/pages/posts/index.tsx +104 -0
  31. package/templates/complex-routing/src/pages/search.tsx +71 -0
  32. package/templates/complex-routing/src/pages/user.tsx +32 -0
  33. package/templates/complex-routing/tsconfig.json +13 -0
  34. package/templates/configured-server-fns/ev.config.ts +56 -0
  35. package/templates/configured-server-fns/index.html +11 -0
  36. package/templates/configured-server-fns/package.json +21 -0
  37. package/templates/configured-server-fns/src/api/users.server.ts +22 -0
  38. package/templates/configured-server-fns/src/main.tsx +12 -0
  39. package/templates/configured-server-fns/src/routes.tsx +111 -0
  40. package/templates/configured-server-fns/tsconfig.json +16 -0
@@ -0,0 +1,22 @@
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");
@@ -0,0 +1,28 @@
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
+ });
@@ -0,0 +1,19 @@
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
+ });
@@ -0,0 +1,19 @@
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
+ });
@@ -0,0 +1,63 @@
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
+ });
@@ -0,0 +1,17 @@
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
+ }
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Server Functions Example</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ </body>
11
+ </html>
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "example-basic-server-fns",
3
+ "version": "0.0.1-rc.8",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "ev dev",
7
+ "build": "ev build",
8
+ "check-types": "tsc --noEmit"
9
+ },
10
+ "dependencies": {
11
+ "@evjs/runtime": "^0.0.1-rc.13",
12
+ "react": "^19.2.4",
13
+ "react-dom": "^19.2.4"
14
+ },
15
+ "devDependencies": {
16
+ "@evjs/cli": "^0.0.1-rc.5",
17
+ "@types/react": "^19.1.8",
18
+ "@types/react-dom": "^19.1.8",
19
+ "typescript": "^5.7.3"
20
+ }
21
+ }
@@ -0,0 +1,31 @@
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
+ }
@@ -0,0 +1,12 @@
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");
@@ -0,0 +1,108 @@
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]);
@@ -0,0 +1,16 @@
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
+ }
@@ -0,0 +1,11 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>evjs — Complex Routing</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ </body>
11
+ </html>
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "example-complex-routing",
3
+ "version": "0.0.1-rc.8",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "ev dev",
7
+ "build": "ev build",
8
+ "check-types": "tsc --noEmit"
9
+ },
10
+ "dependencies": {
11
+ "@evjs/runtime": "^0.0.1-rc.13",
12
+ "react": "^19.2.4",
13
+ "react-dom": "^19.2.4"
14
+ },
15
+ "devDependencies": {
16
+ "@evjs/cli": "^0.0.1-rc.5",
17
+ "@types/react": "^19.0.8",
18
+ "@types/react-dom": "^19.0.3",
19
+ "typescript": "^5.7.3"
20
+ }
21
+ }
@@ -0,0 +1,86 @@
1
+ "use server";
2
+
3
+ // ── Mock data ──
4
+
5
+ const posts = [
6
+ {
7
+ id: "1",
8
+ title: "Getting Started with evjs",
9
+ body: "evjs is a zero-config React meta-framework...",
10
+ author: "alice",
11
+ tags: ["intro", "tutorial"],
12
+ },
13
+ {
14
+ id: "2",
15
+ title: "Server Functions Deep Dive",
16
+ body: 'Server functions use the "use server" directive...',
17
+ author: "bob",
18
+ tags: ["server", "advanced"],
19
+ },
20
+ {
21
+ id: "3",
22
+ title: "Routing Patterns",
23
+ body: "evjs uses TanStack Router for type-safe routing...",
24
+ author: "alice",
25
+ tags: ["routing", "tutorial"],
26
+ },
27
+ {
28
+ id: "4",
29
+ title: "WebSocket Transport",
30
+ body: "You can swap HTTP for WebSocket transport...",
31
+ author: "charlie",
32
+ tags: ["transport", "advanced"],
33
+ },
34
+ {
35
+ id: "5",
36
+ title: "Deploying to Edge",
37
+ body: "Run your evjs app on Deno, Bun, or Workers...",
38
+ author: "bob",
39
+ tags: ["deploy", "edge"],
40
+ },
41
+ ];
42
+
43
+ const users: Record<string, { name: string; bio: string }> = {
44
+ alice: { name: "Alice", bio: "Core contributor" },
45
+ bob: { name: "Bob", bio: "Technical writer" },
46
+ charlie: { name: "Charlie", bio: "DevOps engineer" },
47
+ };
48
+
49
+ // ── Server functions ──
50
+
51
+ export async function getPosts(query?: string) {
52
+ await delay(50);
53
+ if (!query) return posts;
54
+ const q = query.toLowerCase();
55
+ return posts.filter(
56
+ (p) =>
57
+ p.title.toLowerCase().includes(q) || p.tags.some((t) => t.includes(q)),
58
+ );
59
+ }
60
+
61
+ export async function getPost(id: string) {
62
+ await delay(50);
63
+ const post = posts.find((p) => p.id === id);
64
+ if (!post) throw new Error(`Post ${id} not found`);
65
+ return post;
66
+ }
67
+
68
+ export async function getUser(username: string) {
69
+ await delay(50);
70
+ const user = users[username];
71
+ if (!user) throw new Error(`User ${username} not found`);
72
+ return { username, ...user };
73
+ }
74
+
75
+ export async function getStats() {
76
+ await delay(50);
77
+ return {
78
+ totalPosts: posts.length,
79
+ totalUsers: Object.keys(users).length,
80
+ tags: [...new Set(posts.flatMap((p) => p.tags))],
81
+ };
82
+ }
83
+
84
+ function delay(ms: number) {
85
+ return new Promise((r) => setTimeout(r, ms));
86
+ }
@@ -0,0 +1,29 @@
1
+ import { createApp } from "@evjs/runtime/client";
2
+ import { rootRoute } from "./pages/__root";
3
+ import { notFoundRoute, redirectRoute } from "./pages/catch";
4
+ import { dashboardLayout, dashboardRoute } from "./pages/dashboard";
5
+ import { homeRoute } from "./pages/home";
6
+ import { postDetailRoute, postsIndexRoute, postsRoute } from "./pages/posts";
7
+ import { searchRoute } from "./pages/search";
8
+ import { userRoute } from "./pages/user";
9
+
10
+ const routeTree = rootRoute.addChildren([
11
+ homeRoute,
12
+ postsRoute.addChildren([postsIndexRoute, postDetailRoute]),
13
+ userRoute,
14
+ dashboardLayout.addChildren([dashboardRoute]),
15
+ searchRoute,
16
+ redirectRoute,
17
+ notFoundRoute,
18
+ ]);
19
+
20
+ const app = createApp({ routeTree });
21
+
22
+ // Register router type for full IDE type-safety on useParams, useSearch, Link, etc.
23
+ declare module "@tanstack/react-router" {
24
+ interface Register {
25
+ router: typeof app.router;
26
+ }
27
+ }
28
+
29
+ app.render("#app");
@@ -0,0 +1,60 @@
1
+ import { createAppRootRoute, Link, Outlet } from "@evjs/runtime/client";
2
+
3
+ const styles = {
4
+ app: {
5
+ fontFamily: "system-ui, sans-serif",
6
+ maxWidth: 900,
7
+ margin: "0 auto",
8
+ padding: "1rem",
9
+ },
10
+ nav: {
11
+ display: "flex",
12
+ gap: "1rem",
13
+ padding: "0.75rem 0",
14
+ borderBottom: "1px solid #e5e7eb",
15
+ marginBottom: "1rem",
16
+ },
17
+ link: { textDecoration: "none", color: "#6b7280" },
18
+ activeLink: { color: "#111", fontWeight: 600 as const },
19
+ };
20
+
21
+ function RootLayout() {
22
+ return (
23
+ <div style={styles.app}>
24
+ <nav style={styles.nav}>
25
+ <Link
26
+ to="/"
27
+ style={styles.link}
28
+ activeProps={{ style: styles.activeLink }}
29
+ >
30
+ Home
31
+ </Link>
32
+ <Link
33
+ to="/posts"
34
+ style={styles.link}
35
+ activeProps={{ style: styles.activeLink }}
36
+ >
37
+ Posts
38
+ </Link>
39
+ <Link
40
+ to="/dashboard"
41
+ style={styles.link}
42
+ activeProps={{ style: styles.activeLink }}
43
+ >
44
+ Dashboard
45
+ </Link>
46
+ <Link
47
+ to="/search"
48
+ search={{ q: "" }}
49
+ style={styles.link}
50
+ activeProps={{ style: styles.activeLink }}
51
+ >
52
+ Search
53
+ </Link>
54
+ </nav>
55
+ <Outlet />
56
+ </div>
57
+ );
58
+ }
59
+
60
+ export const rootRoute = createAppRootRoute({ component: RootLayout });
@@ -0,0 +1,26 @@
1
+ import { createRoute, Link, redirect } from "@evjs/runtime/client";
2
+ import { rootRoute } from "./__root";
3
+
4
+ // ── Redirect: /old-blog → /posts ──
5
+
6
+ export const redirectRoute = createRoute({
7
+ getParentRoute: () => rootRoute,
8
+ path: "/old-blog",
9
+ beforeLoad: () => {
10
+ throw redirect({ to: "/posts" });
11
+ },
12
+ });
13
+
14
+ // ── 404 Catch-all ──
15
+
16
+ export const notFoundRoute = createRoute({
17
+ getParentRoute: () => rootRoute,
18
+ path: "*",
19
+ component: () => (
20
+ <div style={{ textAlign: "center", padding: "3rem" }}>
21
+ <h1 style={{ fontSize: 48 }}>404</h1>
22
+ <p style={{ color: "#6b7280" }}>Page not found</p>
23
+ <Link to="/">← Go home</Link>
24
+ </div>
25
+ ),
26
+ });