@evjs/cli 0.0.1-rc.18 → 0.0.1-rc.28

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 (50) hide show
  1. package/README.md +4 -8
  2. package/dist/create-webpack-config.js +5 -2
  3. package/dist/index.js +5 -91
  4. package/package.json +7 -10
  5. package/templates/basic-csr/README.md +0 -23
  6. package/templates/basic-csr/index.html +0 -11
  7. package/templates/basic-csr/package.json +0 -21
  8. package/templates/basic-csr/src/main.tsx +0 -22
  9. package/templates/basic-csr/src/pages/__root.tsx +0 -28
  10. package/templates/basic-csr/src/pages/about.tsx +0 -19
  11. package/templates/basic-csr/src/pages/home.tsx +0 -19
  12. package/templates/basic-csr/src/pages/posts/index.tsx +0 -63
  13. package/templates/basic-csr/tsconfig.json +0 -17
  14. package/templates/basic-server-fns/README.md +0 -24
  15. package/templates/basic-server-fns/index.html +0 -11
  16. package/templates/basic-server-fns/package.json +0 -21
  17. package/templates/basic-server-fns/src/api/users.server.ts +0 -31
  18. package/templates/basic-server-fns/src/main.tsx +0 -12
  19. package/templates/basic-server-fns/src/routes.tsx +0 -108
  20. package/templates/basic-server-fns/tsconfig.json +0 -16
  21. package/templates/complex-routing/README.md +0 -27
  22. package/templates/complex-routing/index.html +0 -11
  23. package/templates/complex-routing/package.json +0 -21
  24. package/templates/complex-routing/src/api/data.server.ts +0 -86
  25. package/templates/complex-routing/src/main.tsx +0 -29
  26. package/templates/complex-routing/src/pages/__root.tsx +0 -60
  27. package/templates/complex-routing/src/pages/catch.tsx +0 -26
  28. package/templates/complex-routing/src/pages/dashboard.tsx +0 -81
  29. package/templates/complex-routing/src/pages/home.tsx +0 -35
  30. package/templates/complex-routing/src/pages/posts/index.tsx +0 -104
  31. package/templates/complex-routing/src/pages/search.tsx +0 -71
  32. package/templates/complex-routing/src/pages/user.tsx +0 -32
  33. package/templates/complex-routing/tsconfig.json +0 -13
  34. package/templates/configured-server-fns/README.md +0 -24
  35. package/templates/configured-server-fns/ev.config.ts +0 -56
  36. package/templates/configured-server-fns/index.html +0 -11
  37. package/templates/configured-server-fns/package.json +0 -21
  38. package/templates/configured-server-fns/src/api/users.server.ts +0 -22
  39. package/templates/configured-server-fns/src/main.tsx +0 -12
  40. package/templates/configured-server-fns/src/routes.tsx +0 -111
  41. package/templates/configured-server-fns/tsconfig.json +0 -16
  42. package/templates/with-tailwind/ev.config.ts +0 -20
  43. package/templates/with-tailwind/index.html +0 -11
  44. package/templates/with-tailwind/package.json +0 -28
  45. package/templates/with-tailwind/postcss.config.mjs +0 -5
  46. package/templates/with-tailwind/src/main.tsx +0 -15
  47. package/templates/with-tailwind/src/pages/__root.tsx +0 -30
  48. package/templates/with-tailwind/src/pages/home.tsx +0 -85
  49. package/templates/with-tailwind/src/styles.css +0 -2
  50. 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
- ### `ev init [name]`
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
 
@@ -59,7 +55,7 @@ export default defineConfig({
59
55
  },
60
56
  server: {
61
57
  endpoint: "/api/fn",
62
- middleware: [],
58
+
63
59
  dev: { port: 3001 },
64
60
  },
65
61
  });
@@ -81,7 +77,7 @@ my-app/
81
77
  ├── api/ # server functions
82
78
  │ ├── users.server.ts
83
79
  │ └── posts.server.ts
84
- └── middleware/ # server middleware (optional)
80
+
85
81
  └── auth.ts
86
82
  ```
87
83
 
@@ -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/runtime`
89
+ 4. **Import `defineConfig` from `@evjs/cli`** — not from `@evjs/server`
94
90
 
95
91
  ## Bundled Dependencies
96
92
 
@@ -19,8 +19,8 @@ export function createWebpackConfig(config, cwd) {
19
19
  const isProduction = process.env.NODE_ENV === "production";
20
20
  const HtmlWebpackPlugin = esmRequire("html-webpack-plugin");
21
21
  const { EvWebpackPlugin } = esmRequire("@evjs/webpack-plugin");
22
- const pluginOptions = server?.middleware?.length
23
- ? { server: { middleware: server.middleware } }
22
+ const pluginOptions = server
23
+ ? { server: { entry: server.entry } }
24
24
  : undefined;
25
25
  // Resolve loader paths from evjs's dependency tree so they work
26
26
  // even when the user's project doesn't list them as direct deps.
@@ -93,6 +93,9 @@ export function createWebpackConfig(config, cwd) {
93
93
  plugins: [
94
94
  new HtmlWebpackPlugin({ template: html }),
95
95
  new EvWebpackPlugin(pluginOptions),
96
+ ...(!isProduction
97
+ ? [new (esmRequire("webpack").HotModuleReplacementPlugin)()]
98
+ : []),
96
99
  ],
97
100
  optimization: isProduction
98
101
  ? { splitChunks: { chunks: "all" } }
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
  *
@@ -149,8 +62,8 @@ program
149
62
  const bootstrapPath = path.resolve(cwd, "dist/server/_dev_start.cjs");
150
63
  if (fs.existsSync(manifestPath)) {
151
64
  const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
152
- // Only start API server if there are actual server functions
153
- if (Object.keys(manifest.server?.fns ?? {}).length === 0)
65
+ // Start API server if there's a server entry
66
+ if (!manifest.server?.entry)
154
67
  return;
155
68
  apiStarted = true;
156
69
  const backendConfig = evjsConfig?.server?.backend ?? "node";
@@ -158,10 +71,11 @@ program
158
71
  logger.info `Server bundle detected, starting ${backend} API...`;
159
72
  try {
160
73
  const serverBundlePath = path.resolve(cwd, "dist/server", manifest.server.entry);
74
+ fs.ensureDirSync(path.dirname(bootstrapPath));
161
75
  fs.writeFileSync(bootstrapPath, [
162
76
  `const bundle = require(${JSON.stringify(serverBundlePath)});`,
163
- `const app = bundle.createApp({ endpoint: ${JSON.stringify(evjsConfig?.server?.endpoint ?? CONFIG_DEFAULTS.endpoint)} });`,
164
- `const { serve } = require("@evjs/runtime/server/node");`,
77
+ `const app = bundle.app || bundle.createApp({ endpoint: ${JSON.stringify(evjsConfig?.server?.endpoint ?? CONFIG_DEFAULTS.endpoint)} });`,
78
+ `const { serve } = require("@evjs/server/node");`,
165
79
  `serve(app, { port: ${serverPort} });`,
166
80
  ].join("\n"));
167
81
  // 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.18",
3
+ "version": "0.0.1-rc.28",
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",
@@ -31,14 +30,13 @@
31
30
  "dependencies": {
32
31
  "@evjs/webpack-plugin": "*",
33
32
  "@logtape/logtape": "^2.0.4",
34
- "@swc/core": "^1.15.18",
33
+ "@swc/core": "^1.15.21",
35
34
  "commander": "^12.1.0",
36
- "execa": "^9.5.2",
37
- "fs-extra": "^11.3.0",
35
+ "execa": "^9.6.1",
36
+ "fs-extra": "^11.3.4",
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",
@@ -46,9 +44,8 @@
46
44
  },
47
45
  "devDependencies": {
48
46
  "@types/fs-extra": "^11.0.4",
49
- "@types/node": "^22.13.5",
50
- "@types/prompts": "^2.4.9",
51
- "typescript": "^5.7.3"
47
+ "@types/node": "^22.19.15",
48
+ "typescript": "^6.0.2"
52
49
  },
53
50
  "homepage": "https://github.com/evaijs/evjs#readme",
54
51
  "bugs": {
@@ -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,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>ev — Basic CSR Example</title>
7
- </head>
8
- <body>
9
- <div id="app"></div>
10
- </body>
11
- </html>
@@ -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,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>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-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");