@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
package/AGENT.md ADDED
@@ -0,0 +1,121 @@
1
+ # @evjs/cli — Agent Guide
2
+
3
+ > AI-agent reference for developing apps with the `@evjs/cli` package.
4
+
5
+ ## Overview
6
+
7
+ `@evjs/cli` is the CLI and configuration layer. Users install it as a devDependency. It provides:
8
+
9
+ - **`ev init`** — scaffold a new project from templates
10
+ - **`ev dev`** — start dev server (webpack-dev-server + API server)
11
+ - **`ev build`** — production build (client + server bundles)
12
+ - **`defineConfig`** — type-safe config export for `ev.config.ts`
13
+
14
+ ## Quick Start
15
+
16
+ ```bash
17
+ npx @evjs/cli@latest init my-app
18
+ cd my-app && npm install
19
+ ev dev # http://localhost:3000
20
+ ```
21
+
22
+ ## Configuration (`ev.config.ts`)
23
+
24
+ Optional — everything works zero-config. Create `ev.config.ts` in project root when needed:
25
+
26
+ ```ts
27
+ import { defineConfig } from "@evjs/cli";
28
+
29
+ export default defineConfig({
30
+ client: {
31
+ entry: "./src/main.tsx", // default
32
+ html: "./index.html", // default
33
+ dev: {
34
+ port: 3000, // webpack-dev-server port
35
+ open: true, // auto-open browser
36
+ https: false, // enable HTTPS
37
+ historyApiFallback: true, // SPA routing fallback
38
+ },
39
+ transport: {
40
+ baseUrl: "", // API base URL (for separate API host)
41
+ endpoint: "/api/fn", // server function endpoint path
42
+ },
43
+ },
44
+ server: {
45
+ endpoint: "/api/fn", // must match client transport endpoint
46
+ runner: "@evjs/runtime/server/node",
47
+ middleware: [
48
+ "./src/middleware/auth.ts", // middleware module paths
49
+ "./src/middleware/logging.ts",
50
+ ],
51
+ dev: {
52
+ port: 3001, // API server port in dev mode
53
+ },
54
+ },
55
+ });
56
+ ```
57
+
58
+ ### Config Defaults
59
+
60
+ | Key | Default |
61
+ |-----|---------|
62
+ | `client.entry` | `"./src/main.tsx"` |
63
+ | `client.html` | `"./index.html"` |
64
+ | `client.dev.port` | `3000` |
65
+ | `server.endpoint` | `"/api/fn"` |
66
+ | `server.dev.port` | `3001` |
67
+ | `server.runner` | `"@evjs/runtime/server/node"` |
68
+
69
+ ## Project Structure
70
+
71
+ ```
72
+ my-app/
73
+ ├── ev.config.ts # optional config
74
+ ├── index.html # HTML template
75
+ ├── package.json
76
+ ├── tsconfig.json
77
+ └── src/
78
+ ├── main.tsx # app bootstrap (keep minimal)
79
+ ├── routes.tsx # route tree + components
80
+ ├── api/ # server functions
81
+ │ ├── users.server.ts
82
+ │ └── posts.server.ts
83
+ └── middleware/ # server middleware (optional)
84
+ └── auth.ts
85
+ ```
86
+
87
+ ## CLI Commands
88
+
89
+ ### `ev init [project-name]`
90
+ Interactive scaffolding. Templates:
91
+ - `basic-csr` — client-side rendering only
92
+ - `basic-server-fns` — server functions example
93
+ - `configured-server-fns` — advanced config example
94
+ - `complex-routing` — params, search, layouts, loaders
95
+
96
+ ### `ev dev`
97
+ - Starts webpack-dev-server on port 3000
98
+ - Auto-starts API server on port 3001
99
+ - Proxies `/api/fn` requests to API server
100
+ - Hot reloads client; restarts server on changes
101
+ - `NODE_ENV=development`
102
+
103
+ ### `ev build`
104
+ - Outputs client bundle to `dist/client/`
105
+ - Outputs server bundle to `dist/server/`
106
+ - Emits `dist/server/manifest.json` with server function registry
107
+ - `NODE_ENV=production`
108
+
109
+ ## Common Mistakes
110
+
111
+ 1. **Don't create `webpack.config.cjs`** — use `ev.config.ts` instead
112
+ 2. **Don't install webpack manually** — it's a dependency of `@evjs/cli`
113
+ 3. **Config file must be `ev.config.ts`** — not `evjs.config.ts` or `evjs.config.ts`
114
+ 4. **Import `defineConfig` from `@evjs/cli`** — not from `@evjs/runtime`
115
+
116
+ ## Dependencies (bundled)
117
+
118
+ Users do NOT need to install these — they're included in `@evjs/cli`:
119
+ - `webpack`, `webpack-dev-server`, `webpack-cli`
120
+ - `html-webpack-plugin`, `swc-loader`, `@swc/core`
121
+ - `@evjs/webpack-plugin`
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # @evjs/cli
2
+
3
+ > CLI and configuration for the **@evjs/cli** meta-framework.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @evjs/cli
9
+ ```
10
+
11
+ ## Zero-Config
12
+
13
+ No configuration file is needed. `ev dev` and `ev build` work out of the box with sensible defaults:
14
+
15
+ - Entry: `./src/main.tsx`
16
+ - HTML: `./index.html`
17
+ - Client dev server: port 3000
18
+ - API server (dev): port 3001
19
+ - Server functions auto-discovered via `"use server"` directive
20
+
21
+ ## Commands
22
+
23
+ | Command | Description |
24
+ |---------|-------------|
25
+ | `ev init [name]` | Scaffold a new project from a template |
26
+ | `ev dev` | Start dev server (client HMR + API watch) |
27
+ | `ev build` | Production build (client + server) |
28
+
29
+ ### `ev init [name]`
30
+
31
+ Templates: `basic-csr`, `basic-server-fns`, `trpc-server-fns`.
32
+ Option: `-t, --template <template>` to skip interactive selection.
33
+
34
+ ### `ev dev`
35
+
36
+ Uses webpack Node API directly (no temp config files):
37
+ 1. **WebpackDevServer** (port 3000) — client bundle with HMR.
38
+ 2. **Node API Server** (port 3001) — auto-starts when server bundle is emitted, uses `node --watch`.
39
+
40
+ ### `ev build`
41
+
42
+ Runs webpack via Node API with `NODE_ENV=production`:
43
+ - `dist/client/` — optimized client assets with content hashes.
44
+ - `dist/server/main.[hash].js` — server bundle (entry discovered via `dist/server/manifest.json`).
45
+
46
+ ## Configuration
47
+
48
+ Create `ev.config.ts` in the project root (optional):
49
+
50
+ ```ts
51
+ import { defineConfig } from "@evjs/cli";
52
+
53
+ export default defineConfig({
54
+ client: {
55
+ entry: "./src/main.tsx",
56
+ html: "./index.html",
57
+ dev: { port: 3000 },
58
+ },
59
+ server: {
60
+ endpoint: "/api/fn",
61
+ middleware: [],
62
+ dev: { port: 3001 },
63
+ },
64
+ });
65
+ ```
66
+
67
+ The `client.dev` and `server.dev` fields accept extra options that are merged with defaults.
package/dist/config.js ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Default configuration values.
3
+ *
4
+ * Single source of truth for all defaults across the framework.
5
+ */
6
+ export const CONFIG_DEFAULTS = {
7
+ entry: "./src/main.tsx",
8
+ html: "./index.html",
9
+ clientPort: 3000,
10
+ serverPort: 3001,
11
+ endpoint: "/api/fn",
12
+ };
13
+ /**
14
+ * Define configuration for the evjs framework.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * // ev.config.ts
19
+ * import { defineConfig } from "@evjs/cli";
20
+ *
21
+ * export default defineConfig({
22
+ * client: {
23
+ * entry: "./src/main.tsx",
24
+ * dev: { port: 3000 },
25
+ * },
26
+ * server: {
27
+ * endpoint: "/api/fn",
28
+ * dev: { port: 3001 },
29
+ * },
30
+ * });
31
+ * ```
32
+ */
33
+ export function defineConfig(config) {
34
+ return config;
35
+ }
@@ -0,0 +1,99 @@
1
+ import { createRequire } from "node:module";
2
+ import path from "node:path";
3
+ import { CONFIG_DEFAULTS } from "./config.js";
4
+ const esmRequire = createRequire(import.meta.url);
5
+ /**
6
+ * Create a webpack configuration object from EvfConfig.
7
+ *
8
+ * Returns a plain object that can be passed directly to the webpack Node API.
9
+ * No temp files are generated.
10
+ */
11
+ export function createWebpackConfig(config, cwd) {
12
+ const client = config?.client;
13
+ const server = config?.server;
14
+ const entry = client?.entry ?? CONFIG_DEFAULTS.entry;
15
+ const html = client?.html ?? CONFIG_DEFAULTS.html;
16
+ const clientPort = client?.dev?.port ?? CONFIG_DEFAULTS.clientPort;
17
+ const serverPort = server?.dev?.port ?? CONFIG_DEFAULTS.serverPort;
18
+ const endpoint = server?.endpoint ?? CONFIG_DEFAULTS.endpoint;
19
+ const isProduction = process.env.NODE_ENV === "production";
20
+ const HtmlWebpackPlugin = esmRequire("html-webpack-plugin");
21
+ const { EvWebpackPlugin } = esmRequire("@evjs/webpack-plugin");
22
+ const pluginOptions = server?.middleware?.length
23
+ ? { server: { middleware: server.middleware } }
24
+ : undefined;
25
+ // Resolve loader paths from evjs's dependency tree so they work
26
+ // even when the user's project doesn't list them as direct deps.
27
+ const resolveLoader = (id) => {
28
+ try {
29
+ return esmRequire.resolve(id);
30
+ }
31
+ catch {
32
+ return id;
33
+ }
34
+ };
35
+ // Derive the proxy base path from the configured endpoint.
36
+ // e.g. "/api/fn" → "/api", "/rpc/v1" → "/rpc"
37
+ const proxyBase = `/${endpoint.split("/").filter(Boolean)[0] || "api"}`;
38
+ // Destructure port out of dev overrides to avoid passing it twice.
39
+ const { port: _p, ...devServerOverrides } = client?.dev ?? {};
40
+ return {
41
+ name: "client",
42
+ mode: isProduction ? "production" : "development",
43
+ devtool: isProduction ? "hidden-source-map" : "source-map",
44
+ entry,
45
+ output: {
46
+ path: path.resolve(cwd, "dist/client"),
47
+ filename: isProduction ? "[name].[contenthash:8].js" : "index.js",
48
+ clean: true,
49
+ },
50
+ resolve: {
51
+ extensions: [".tsx", ".ts", ".js"],
52
+ },
53
+ module: {
54
+ rules: [
55
+ {
56
+ test: /\.m?js/,
57
+ resolve: { fullySpecified: false },
58
+ },
59
+ {
60
+ test: /\.tsx?$/,
61
+ exclude: /node_modules/,
62
+ use: [
63
+ {
64
+ loader: resolveLoader("swc-loader"),
65
+ options: {
66
+ jsc: {
67
+ parser: { syntax: "typescript", tsx: true },
68
+ transform: { react: { runtime: "automatic" } },
69
+ },
70
+ },
71
+ },
72
+ {
73
+ loader: resolveLoader("@evjs/webpack-plugin/server-fn-loader"),
74
+ },
75
+ ],
76
+ },
77
+ ],
78
+ },
79
+ plugins: [
80
+ new HtmlWebpackPlugin({ template: html }),
81
+ new EvWebpackPlugin(pluginOptions),
82
+ ],
83
+ optimization: isProduction
84
+ ? { splitChunks: { chunks: "all" } }
85
+ : undefined,
86
+ devServer: {
87
+ port: clientPort,
88
+ hot: true,
89
+ devMiddleware: { writeToDisk: true },
90
+ proxy: [
91
+ {
92
+ context: [proxyBase],
93
+ target: `http://localhost:${serverPort}`,
94
+ },
95
+ ],
96
+ ...devServerOverrides,
97
+ },
98
+ };
99
+ }
package/dist/index.js ADDED
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { configure, getConsoleSink, getLogger } from "@logtape/logtape";
6
+ import { Command } from "commander";
7
+ import { execa } from "execa";
8
+ import fs from "fs-extra";
9
+ import prompts from "prompts";
10
+ import { CONFIG_DEFAULTS } from "./config.js";
11
+ const esmRequire = createRequire(import.meta.url);
12
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
+ await configure({
14
+ sinks: { console: getConsoleSink() },
15
+ loggers: [
16
+ { category: ["logtape", "meta"], lowestLevel: "warning" },
17
+ { category: ["evjs"], sinks: ["console"], lowestLevel: "info" },
18
+ ],
19
+ });
20
+ const logger = getLogger(["evjs", "cli"]);
21
+ const pkg = fs.readJsonSync(path.resolve(__dirname, "../package.json"));
22
+ const program = new Command();
23
+ program
24
+ .name("ev")
25
+ .description("CLI for the evjs framework")
26
+ .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
+ },
57
+ ], {
58
+ onCancel: () => {
59
+ process.exit(1);
60
+ },
61
+ });
62
+ const projectName = response.projectName || name;
63
+ const template = response.template || options.template;
64
+ const targetDir = path.resolve(process.cwd(), projectName);
65
+ if (fs.existsSync(targetDir)) {
66
+ logger.error `Directory ${projectName} already exists!`;
67
+ process.exit(1);
68
+ }
69
+ const templateDir = path.resolve(__dirname, "../templates", template);
70
+ if (!fs.existsSync(templateDir)) {
71
+ logger.error `Template ${template} not found!`;
72
+ process.exit(1);
73
+ }
74
+ logger.info `Scaffolding project in ${targetDir}...`;
75
+ await fs.copy(templateDir, targetDir, {
76
+ dereference: true,
77
+ filter: (src) => {
78
+ const basename = path.basename(src);
79
+ return !["node_modules", "dist", ".turbo"].includes(basename);
80
+ },
81
+ });
82
+ // Post-process package.json: sync @evjs/* versions and set project name
83
+ const pkgPath = path.join(targetDir, "package.json");
84
+ if (fs.existsSync(pkgPath)) {
85
+ const pkg = await fs.readJson(pkgPath);
86
+ pkg.name = projectName;
87
+ delete pkg.private; // Templates shouldn't be private by default
88
+ const updateDeps = (deps) => {
89
+ if (!deps)
90
+ return;
91
+ for (const [name, val] of Object.entries(deps)) {
92
+ // Sync all @evjs/* packages to current CLI version
93
+ if (name.startsWith("@evjs/") &&
94
+ (val === "*" ||
95
+ (typeof val === "string" && val.includes("workspace")))) {
96
+ deps[name] = `^${pkg.version}`;
97
+ }
98
+ }
99
+ };
100
+ updateDeps(pkg.dependencies);
101
+ updateDeps(pkg.devDependencies);
102
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
103
+ }
104
+ logger.info `Done! Now run:`;
105
+ logger.info ` cd ${projectName}`;
106
+ logger.info ` npm install`;
107
+ logger.info ` npm run dev`;
108
+ });
109
+ /**
110
+ * Load config and create webpack configuration object.
111
+ *
112
+ * Uses ev.config.ts when present, otherwise falls back to zero-config defaults.
113
+ * No webpack.config.cjs fallback — the meta-framework owns the build config.
114
+ */
115
+ async function resolveWebpackConfig(cwd) {
116
+ const { loadConfig } = await import("./load-config.js");
117
+ const evjsConfig = await loadConfig(cwd);
118
+ const { createWebpackConfig } = await import("./create-webpack-config.js");
119
+ logger.info `Using ${evjsConfig ? "ev.config.ts" : "zero-config defaults"}`;
120
+ const webpackConfig = createWebpackConfig(evjsConfig, cwd);
121
+ return { evjsConfig, webpackConfig };
122
+ }
123
+ program
124
+ .command("dev")
125
+ .description("Start development server")
126
+ .action(async () => {
127
+ const cwd = process.cwd();
128
+ process.env.NODE_ENV ??= "development";
129
+ const { evjsConfig, webpackConfig } = await resolveWebpackConfig(cwd);
130
+ const serverPort = evjsConfig?.server?.dev?.port ?? CONFIG_DEFAULTS.serverPort;
131
+ logger.info `Starting development server...`;
132
+ try {
133
+ const webpack = esmRequire("webpack");
134
+ const WebpackDevServer = esmRequire("webpack-dev-server");
135
+ const compiler = webpack(webpackConfig);
136
+ const devServerOptions = webpackConfig.devServer ?? {};
137
+ const server = new WebpackDevServer(devServerOptions, compiler);
138
+ await server.start();
139
+ // Background: wait for server bundle, then start Node API
140
+ void (async () => {
141
+ const manifestPath = path.resolve(cwd, "dist/server/manifest.json");
142
+ const bootstrapPath = path.resolve(cwd, "dist/server/_dev_start.cjs");
143
+ let started = false;
144
+ while (true) {
145
+ if (fs.existsSync(manifestPath)) {
146
+ if (!started) {
147
+ const runnerConfig = evjsConfig?.server?.runner ?? "node";
148
+ const [runner, ...runnerExtraArgs] = runnerConfig.split(/\s+/);
149
+ logger.info `Server bundle detected, starting ${runner} API...`;
150
+ started = true;
151
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
152
+ const serverBundlePath = path.resolve(cwd, "dist/server", manifest.entry);
153
+ fs.writeFileSync(bootstrapPath, [
154
+ `const bundle = require(${JSON.stringify(serverBundlePath)});`,
155
+ `const app = bundle.createApp({ endpoint: ${JSON.stringify(evjsConfig?.server?.endpoint ?? CONFIG_DEFAULTS.endpoint)} });`,
156
+ `const { serve } = require("@evjs/runtime/server/node");`,
157
+ `serve(app, { port: ${serverPort} });`,
158
+ ].join("\n"));
159
+ // node gets --watch flags; other runtimes use their own args as-is
160
+ const runnerArgs = runner === "node"
161
+ ? [
162
+ "--watch",
163
+ "--watch-preserve-output",
164
+ ...runnerExtraArgs,
165
+ bootstrapPath,
166
+ ]
167
+ : [...runnerExtraArgs, bootstrapPath];
168
+ try {
169
+ await execa(runner, runnerArgs, {
170
+ stdio: "inherit",
171
+ env: { ...process.env, NODE_ENV: "development" },
172
+ });
173
+ }
174
+ catch (_e) {
175
+ started = false;
176
+ }
177
+ }
178
+ }
179
+ await new Promise((r) => setTimeout(r, 500));
180
+ }
181
+ })().catch((err) => {
182
+ logger.error `Server runner failed: ${err}`;
183
+ process.exit(1);
184
+ });
185
+ }
186
+ catch (err) {
187
+ logger.error `Dev server failed to start: ${err}`;
188
+ process.exit(1);
189
+ }
190
+ });
191
+ program
192
+ .command("build")
193
+ .description("Build project for production")
194
+ .action(async () => {
195
+ const cwd = process.cwd();
196
+ process.env.NODE_ENV ??= "production";
197
+ const { webpackConfig } = await resolveWebpackConfig(cwd);
198
+ logger.info `Building for production...`;
199
+ const webpack = esmRequire("webpack");
200
+ const compiler = webpack(webpackConfig);
201
+ await new Promise((resolve, reject) => {
202
+ compiler.run((err, stats) => {
203
+ if (err) {
204
+ reject(err);
205
+ return;
206
+ }
207
+ console.log(stats.toString({
208
+ colors: true,
209
+ modules: false,
210
+ children: true,
211
+ }));
212
+ if (stats.hasErrors()) {
213
+ process.exit(1);
214
+ }
215
+ compiler.close(() => resolve());
216
+ });
217
+ });
218
+ logger.info `Build complete!`;
219
+ });
220
+ program.parse();
@@ -0,0 +1,39 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ const CONFIG_FILES = ["ev.config.ts", "ev.config.js", "ev.config.mjs"];
4
+ /**
5
+ * Ensure a TypeScript loader is registered before importing `.ts` config files.
6
+ * Tries `@swc-node/register/esm-register` (ships alongside `@swc/core` which
7
+ * the CLI already bundles), then falls back to Node's built-in `--loader tsx`
8
+ * pathway. If neither is available the raw `import()` is attempted anyway —
9
+ * Node will throw a clear error telling the user to install a loader.
10
+ */
11
+ async function ensureTsLoader() {
12
+ try {
13
+ // @ts-expect-error — optional dependency, may not be installed
14
+ await import("@swc-node/register/esm-register");
15
+ }
16
+ catch {
17
+ // Loader not available — Node may still handle .ts via --loader flag
18
+ }
19
+ }
20
+ /**
21
+ * Load evjs config from the project root.
22
+ *
23
+ * Looks for `ev.config.ts`, `.js`, or `.mjs` in the given directory.
24
+ * Returns undefined if no config file is found.
25
+ */
26
+ export async function loadConfig(cwd) {
27
+ for (const filename of CONFIG_FILES) {
28
+ const configPath = path.resolve(cwd, filename);
29
+ if (fs.existsSync(configPath)) {
30
+ // Register TS loader for .ts config files
31
+ if (filename.endsWith(".ts")) {
32
+ await ensureTsLoader();
33
+ }
34
+ const mod = await import(configPath);
35
+ return mod.default ?? mod;
36
+ }
37
+ }
38
+ return undefined;
39
+ }
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@evjs/cli",
3
+ "version": "0.0.1-rc.13",
4
+ "description": "CLI and configuration layer for the evjs framework",
5
+ "type": "module",
6
+ "main": "./dist/config.js",
7
+ "types": "./dist/config.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/config.d.ts",
11
+ "import": "./dist/config.js"
12
+ }
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "bin": {
18
+ "ev": "dist/index.js"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "templates",
23
+ "AGENT.md"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsc",
27
+ "dev": "tsc -w",
28
+ "test": "vitest run",
29
+ "check-types": "tsc --noEmit",
30
+ "prepare": "npm run build"
31
+ },
32
+ "dependencies": {
33
+ "@evjs/webpack-plugin": "0.0.1-rc.13",
34
+ "@logtape/logtape": "^2.0.4",
35
+ "@swc/core": "^1.2.147",
36
+ "commander": "^12.1.0",
37
+ "execa": "^9.5.2",
38
+ "fs-extra": "^11.3.0",
39
+ "glob": "^13.0.6",
40
+ "html-webpack-plugin": "^5.6.6",
41
+ "picocolors": "^1.1.1",
42
+ "prompts": "^2.4.2",
43
+ "swc-loader": "^0.2.7",
44
+ "webpack": "^5.105.4",
45
+ "webpack-cli": "^6.0.1",
46
+ "webpack-dev-server": "^5.2.3"
47
+ },
48
+ "devDependencies": {
49
+ "@types/fs-extra": "^11.0.4",
50
+ "@types/node": "^22.13.5",
51
+ "@types/prompts": "^2.4.9",
52
+ "typescript": "^5.7.3"
53
+ },
54
+ "homepage": "https://github.com/evaijs/evjs#readme",
55
+ "bugs": {
56
+ "url": "https://github.com/evaijs/evjs/issues"
57
+ },
58
+ "repository": {
59
+ "type": "git",
60
+ "url": "git+https://github.com/evaijs/evjs.git"
61
+ },
62
+ "keywords": [
63
+ "evjs",
64
+ "cli",
65
+ "react",
66
+ "server-functions",
67
+ "scaffolding",
68
+ "dev-server",
69
+ "typescript",
70
+ "full-stack",
71
+ "framework"
72
+ ],
73
+ "license": "MIT",
74
+ "author": "xusd320"
75
+ }
@@ -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>ev — Basic CSR Example</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ </body>
11
+ </html>
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "example-basic-csr",
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
+ }