@evjs/cli 0.0.1-rc.17 → 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.
Files changed (37) hide show
  1. package/README.md +3 -6
  2. package/dist/create-webpack-config.js +14 -0
  3. package/dist/index.js +5 -85
  4. package/package.json +2 -5
  5. package/templates/basic-csr/index.html +0 -11
  6. package/templates/basic-csr/package.json +0 -21
  7. package/templates/basic-csr/src/main.tsx +0 -22
  8. package/templates/basic-csr/src/pages/__root.tsx +0 -28
  9. package/templates/basic-csr/src/pages/about.tsx +0 -19
  10. package/templates/basic-csr/src/pages/home.tsx +0 -19
  11. package/templates/basic-csr/src/pages/posts/index.tsx +0 -63
  12. package/templates/basic-csr/tsconfig.json +0 -17
  13. package/templates/basic-server-fns/index.html +0 -11
  14. package/templates/basic-server-fns/package.json +0 -21
  15. package/templates/basic-server-fns/src/api/users.server.ts +0 -31
  16. package/templates/basic-server-fns/src/main.tsx +0 -12
  17. package/templates/basic-server-fns/src/routes.tsx +0 -108
  18. package/templates/basic-server-fns/tsconfig.json +0 -16
  19. package/templates/complex-routing/index.html +0 -11
  20. package/templates/complex-routing/package.json +0 -21
  21. package/templates/complex-routing/src/api/data.server.ts +0 -86
  22. package/templates/complex-routing/src/main.tsx +0 -29
  23. package/templates/complex-routing/src/pages/__root.tsx +0 -60
  24. package/templates/complex-routing/src/pages/catch.tsx +0 -26
  25. package/templates/complex-routing/src/pages/dashboard.tsx +0 -81
  26. package/templates/complex-routing/src/pages/home.tsx +0 -35
  27. package/templates/complex-routing/src/pages/posts/index.tsx +0 -104
  28. package/templates/complex-routing/src/pages/search.tsx +0 -71
  29. package/templates/complex-routing/src/pages/user.tsx +0 -32
  30. package/templates/complex-routing/tsconfig.json +0 -13
  31. package/templates/configured-server-fns/ev.config.ts +0 -56
  32. package/templates/configured-server-fns/index.html +0 -11
  33. package/templates/configured-server-fns/package.json +0 -21
  34. package/templates/configured-server-fns/src/api/users.server.ts +0 -22
  35. package/templates/configured-server-fns/src/main.tsx +0 -12
  36. package/templates/configured-server-fns/src/routes.tsx +0 -111
  37. package/templates/configured-server-fns/tsconfig.json +0 -16
@@ -1,86 +0,0 @@
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
- }
@@ -1,29 +0,0 @@
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");
@@ -1,60 +0,0 @@
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 });
@@ -1,26 +0,0 @@
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
- });
@@ -1,81 +0,0 @@
1
- import { createRoute, Outlet, query } from "@evjs/runtime/client";
2
- import { getStats } from "../api/data.server";
3
- import { rootRoute } from "./__root";
4
-
5
- const styles = {
6
- card: {
7
- border: "1px solid #e5e7eb",
8
- borderRadius: 8,
9
- padding: "1rem",
10
- marginBottom: "0.75rem",
11
- },
12
- stat: { textAlign: "center" as const, padding: "1rem" },
13
- tag: {
14
- display: "inline-block",
15
- background: "#f3f4f6",
16
- borderRadius: 4,
17
- padding: "2px 8px",
18
- fontSize: 12,
19
- marginRight: 4,
20
- },
21
- };
22
-
23
- // ── Pathless layout (no URL segment, just shared UI) ──
24
-
25
- export const dashboardLayout = createRoute({
26
- getParentRoute: () => rootRoute,
27
- id: "dashboard-layout",
28
- component: () => (
29
- <div>
30
- <Outlet />
31
- </div>
32
- ),
33
- });
34
-
35
- // ── Dashboard page (/dashboard) ──
36
-
37
- function Dashboard() {
38
- const { data: stats } = query(getStats).useQuery();
39
- if (!stats) return <p>Loading...</p>;
40
- return (
41
- <div>
42
- <h2>Dashboard</h2>
43
- <div style={{ display: "flex", gap: "1rem" }}>
44
- <div style={{ ...styles.card, ...styles.stat }}>
45
- <div style={{ fontSize: 32, fontWeight: 700 }}>
46
- {stats.totalPosts}
47
- </div>
48
- <div style={{ color: "#6b7280" }}>Posts</div>
49
- </div>
50
- <div style={{ ...styles.card, ...styles.stat }}>
51
- <div style={{ fontSize: 32, fontWeight: 700 }}>
52
- {stats.totalUsers}
53
- </div>
54
- <div style={{ color: "#6b7280" }}>Users</div>
55
- </div>
56
- <div style={{ ...styles.card, ...styles.stat }}>
57
- <div style={{ fontSize: 32, fontWeight: 700 }}>
58
- {stats.tags.length}
59
- </div>
60
- <div style={{ color: "#6b7280" }}>Tags</div>
61
- </div>
62
- </div>
63
- <h3>All Tags</h3>
64
- <div>
65
- {stats.tags.map((t) => (
66
- <span key={t} style={styles.tag}>
67
- {t}
68
- </span>
69
- ))}
70
- </div>
71
- </div>
72
- );
73
- }
74
-
75
- export const dashboardRoute = createRoute({
76
- getParentRoute: () => dashboardLayout,
77
- path: "/dashboard",
78
- loader: ({ context }) =>
79
- context.queryClient.ensureQueryData(query(getStats).queryOptions()),
80
- component: Dashboard,
81
- });
@@ -1,35 +0,0 @@
1
- import { createRoute, Link } from "@evjs/runtime/client";
2
- import { rootRoute } from "./__root";
3
-
4
- export const homeRoute = createRoute({
5
- getParentRoute: () => rootRoute,
6
- path: "/",
7
- component: () => (
8
- <div>
9
- <h1>evjs Complex Routing Example</h1>
10
- <p>
11
- Demonstrates nested layouts, dynamic params, search params, redirects,
12
- and server function loaders.
13
- </p>
14
- <ul>
15
- <li>
16
- <Link to="/posts">Posts</Link> — nested group with dynamic{" "}
17
- <code>$postId</code>
18
- </li>
19
- <li>
20
- <Link to="/dashboard">Dashboard</Link> — pathless layout with server
21
- function loader
22
- </li>
23
- <li>
24
- <Link to="/search" search={{ q: "tutorial" }}>
25
- Search
26
- </Link>{" "}
27
- — search params
28
- </li>
29
- <li>
30
- <Link to="/old-blog">Old Blog</Link> — redirect to /posts
31
- </li>
32
- </ul>
33
- </div>
34
- ),
35
- });
@@ -1,104 +0,0 @@
1
- import { createRoute, Link, Outlet, query } from "@evjs/runtime/client";
2
- import { getPost, getPosts } from "../../api/data.server";
3
- import { rootRoute } from "../__root";
4
-
5
- const styles = {
6
- sidebar: { display: "flex", gap: "1.5rem" },
7
- nav: {
8
- minWidth: 180,
9
- borderRight: "1px solid #e5e7eb",
10
- paddingRight: "1rem",
11
- },
12
- tag: {
13
- display: "inline-block",
14
- background: "#f3f4f6",
15
- borderRadius: 4,
16
- padding: "2px 8px",
17
- fontSize: 12,
18
- marginRight: 4,
19
- },
20
- };
21
-
22
- // ── Posts layout (/posts) ──
23
-
24
- function PostsLayout() {
25
- const { data: posts } = query(getPosts).useQuery();
26
- return (
27
- <div style={styles.sidebar}>
28
- <div style={styles.nav}>
29
- <h3 style={{ marginTop: 0 }}>Posts</h3>
30
- <ul style={{ listStyle: "none", padding: 0 }}>
31
- {posts?.map((p) => (
32
- <li key={p.id} style={{ marginBottom: "0.25rem" }}>
33
- <Link
34
- to="/posts/$postId"
35
- params={{ postId: p.id }}
36
- style={{ textDecoration: "none", color: "#374151" }}
37
- activeProps={{ style: { fontWeight: "bold", color: "#111" } }}
38
- >
39
- {p.title}
40
- </Link>
41
- </li>
42
- ))}
43
- </ul>
44
- </div>
45
- <div style={{ flex: 1 }}>
46
- <Outlet />
47
- </div>
48
- </div>
49
- );
50
- }
51
-
52
- export const postsRoute = createRoute({
53
- getParentRoute: () => rootRoute,
54
- path: "/posts",
55
- component: PostsLayout,
56
- });
57
-
58
- // ── Posts index (/posts/) ──
59
-
60
- export const postsIndexRoute = createRoute({
61
- getParentRoute: () => postsRoute,
62
- path: "/",
63
- component: () => (
64
- <p style={{ color: "#6b7280" }}>← Select a post from the sidebar</p>
65
- ),
66
- });
67
-
68
- // ── Post detail (/posts/$postId) ──
69
-
70
- function PostDetail() {
71
- const { postId } = postDetailRoute.useParams();
72
- const { data: post } = query(getPost).useQuery(postId);
73
-
74
- if (!post) return <p>Loading...</p>;
75
- return (
76
- <div>
77
- <h2>{post.title}</h2>
78
- <p style={{ color: "#6b7280" }}>
79
- by{" "}
80
- <Link to="/users/$username" params={{ username: post.author }}>
81
- {post.author}
82
- </Link>
83
- </p>
84
- <p>{post.body}</p>
85
- <div>
86
- {post.tags.map((tag) => (
87
- <span key={tag} style={styles.tag}>
88
- {tag}
89
- </span>
90
- ))}
91
- </div>
92
- </div>
93
- );
94
- }
95
-
96
- export const postDetailRoute = createRoute({
97
- getParentRoute: () => postsRoute,
98
- path: "$postId",
99
- loader: ({ params, context }) =>
100
- context.queryClient.ensureQueryData(
101
- query(getPost).queryOptions(params.postId),
102
- ),
103
- component: PostDetail,
104
- });
@@ -1,71 +0,0 @@
1
- import { createRoute, Link, query } from "@evjs/runtime/client";
2
- import { getPosts } from "../api/data.server";
3
- import { rootRoute } from "./__root";
4
-
5
- const styles = {
6
- card: {
7
- border: "1px solid #e5e7eb",
8
- borderRadius: 8,
9
- padding: "1rem",
10
- marginBottom: "0.75rem",
11
- },
12
- };
13
-
14
- function SearchPage() {
15
- const { q } = searchRoute.useSearch();
16
- const { data: results } = query(getPosts).useQuery(q || undefined);
17
-
18
- return (
19
- <div>
20
- <h2>Search</h2>
21
- <form
22
- onSubmit={(e) => {
23
- e.preventDefault();
24
- const form = new FormData(e.currentTarget);
25
- const q = form.get("q") as string;
26
- window.location.search = `?q=${encodeURIComponent(q)}`;
27
- }}
28
- >
29
- <input
30
- name="q"
31
- defaultValue={q}
32
- placeholder="Search posts..."
33
- style={{
34
- padding: "0.5rem",
35
- width: 300,
36
- borderRadius: 4,
37
- border: "1px solid #d1d5db",
38
- }}
39
- />
40
- </form>
41
- <div style={{ marginTop: "1rem" }}>
42
- {q && <p style={{ color: "#6b7280" }}>Results for "{q}":</p>}
43
- {results?.map((post) => (
44
- <div key={post.id} style={styles.card}>
45
- <Link
46
- to="/posts/$postId"
47
- params={{ postId: post.id }}
48
- style={{ textDecoration: "none", color: "#111" }}
49
- >
50
- <strong>{post.title}</strong>
51
- </Link>
52
- <p
53
- style={{ margin: "0.25rem 0 0", color: "#6b7280", fontSize: 14 }}
54
- >
55
- {post.body}
56
- </p>
57
- </div>
58
- ))}
59
- </div>
60
- </div>
61
- );
62
- }
63
-
64
- export const searchRoute = createRoute({
65
- getParentRoute: () => rootRoute,
66
- path: "/search",
67
- validateSearch: (search: Record<string, unknown>) => ({
68
- q: (search.q as string) || "",
69
- }),
70
- component: SearchPage,
71
- });
@@ -1,32 +0,0 @@
1
- import { createRoute, Link, query } from "@evjs/runtime/client";
2
- import { getUser } from "../api/data.server";
3
- import { rootRoute } from "./__root";
4
-
5
- const styles = {
6
- card: { border: "1px solid #e5e7eb", borderRadius: 8, padding: "1rem" },
7
- };
8
-
9
- function UserProfile() {
10
- const { username } = userRoute.useParams();
11
- const { data: user } = query(getUser).useQuery(username);
12
-
13
- if (!user) return <p>Loading...</p>;
14
- return (
15
- <div style={styles.card}>
16
- <h2>{user.name}</h2>
17
- <p style={{ color: "#6b7280" }}>@{user.username}</p>
18
- <p>{user.bio}</p>
19
- <Link to="/posts">← Back to posts</Link>
20
- </div>
21
- );
22
- }
23
-
24
- export const userRoute = createRoute({
25
- getParentRoute: () => rootRoute,
26
- path: "/users/$username",
27
- loader: ({ params, context }) =>
28
- context.queryClient.ensureQueryData(
29
- query(getUser).queryOptions(params.username),
30
- ),
31
- component: UserProfile,
32
- });
@@ -1,13 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "jsx": "react-jsx",
7
- "strict": true,
8
- "esModuleInterop": true,
9
- "skipLibCheck": true,
10
- "outDir": "dist"
11
- },
12
- "include": ["src"]
13
- }
@@ -1,56 +0,0 @@
1
- import { defineConfig } from "@evjs/cli";
2
-
3
- /**
4
- * Advanced ev.config.ts example.
5
- *
6
- * This file demonstrates all available configuration options.
7
- * All fields are optional — evjs works out of the box.
8
- */
9
- export default defineConfig({
10
- client: {
11
- // Client entry point (default: "./src/main.tsx")
12
- entry: "./src/main.tsx",
13
-
14
- // HTML template (default: "./index.html")
15
- html: "./index.html",
16
-
17
- // Dev server options (merged with built-in defaults)
18
- dev: {
19
- // Dev server port (default: 3000)
20
- port: 4000,
21
-
22
- // Any dev server options can be added here:
23
- // https: true,
24
- // open: true,
25
- // historyApiFallback: true,
26
- },
27
-
28
- // Transport configuration for server function calls
29
- transport: {
30
- // Base URL for server function endpoint (default: same origin)
31
- // baseUrl: "https://api.example.com",
32
-
33
- // Server function endpoint path (default: "/api/fn")
34
- endpoint: "/api/fn",
35
- },
36
- },
37
-
38
- server: {
39
- // Server function endpoint path (default: "/api/fn")
40
- endpoint: "/api/fn",
41
-
42
- // Server backend module (default: "@evjs/runtime/server/node")
43
- // For Deno/Bun/Workers, use: "@evjs/runtime/server/ecma"
44
- backend: "@evjs/runtime/server/node",
45
-
46
- // Middleware module paths to auto-register in server entry
47
- // These are imported and applied in order
48
- // middleware: ["./src/middleware/auth.ts", "./src/middleware/cors.ts"],
49
-
50
- // Dev server options
51
- dev: {
52
- // API server port in dev mode (default: 3001)
53
- port: 4001,
54
- },
55
- },
56
- });
@@ -1,11 +0,0 @@
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>Configured Server Functions Example</title>
7
- </head>
8
- <body>
9
- <div id="app"></div>
10
- </body>
11
- </html>
@@ -1,21 +0,0 @@
1
- {
2
- "name": "example-configured-server-fns",
3
- "version": "0.0.0",
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,22 +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
- await new Promise((r) => setTimeout(r, 100));
13
- return users;
14
- }
15
-
16
- /** Create a new user. */
17
- export async function createUser(data: { name: string; email: string }) {
18
- await new Promise((r) => setTimeout(r, 50));
19
- const newUser = { id: String(users.length + 1), ...data };
20
- users.push(newUser);
21
- return newUser;
22
- }
@@ -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");