@evjs/cli 0.0.2 → 0.0.4

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/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { configure, getConsoleSink } from "@logtape/logtape";
5
+ import { Command } from "commander";
6
+ import fs from "fs-extra";
7
+ import { build, dev } from "./index.js";
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ await configure({
10
+ sinks: { console: getConsoleSink() },
11
+ loggers: [
12
+ { category: ["logtape", "meta"], lowestLevel: "warning" },
13
+ { category: ["evjs"], sinks: ["console"], lowestLevel: "info" },
14
+ ],
15
+ });
16
+ const pkg = fs.readJsonSync(path.resolve(__dirname, "../package.json"));
17
+ const program = new Command();
18
+ program
19
+ .name("ev")
20
+ .description("CLI for the evjs framework")
21
+ .version(pkg.version);
22
+ program
23
+ .command("dev")
24
+ .description("Start development server")
25
+ .action(async () => {
26
+ const cwd = process.cwd();
27
+ const { loadConfig } = await import("./load-config.js");
28
+ const config = await loadConfig(cwd);
29
+ try {
30
+ await dev(config ?? undefined, { cwd });
31
+ }
32
+ catch (err) {
33
+ console.error(err);
34
+ process.exit(1);
35
+ }
36
+ });
37
+ program
38
+ .command("build")
39
+ .description("Build project for production")
40
+ .action(async () => {
41
+ const cwd = process.cwd();
42
+ const { loadConfig } = await import("./load-config.js");
43
+ const config = await loadConfig(cwd);
44
+ try {
45
+ await build(config ?? undefined, { cwd });
46
+ }
47
+ catch (err) {
48
+ console.error(err);
49
+ process.exit(1);
50
+ }
51
+ });
52
+ program.parse();
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Server configuration.
3
+ *
4
+ * Controls server backend, entry, plugins, and dev options.
5
+ */
6
+ export interface ServerConfig {
7
+ /** Explicit server entry file. If provided, overrides auto-generated entry. */
8
+ entry?: string;
9
+ /** Server backend command. Default: "node". */
10
+ backend?: string;
11
+ /** Server function configuration. */
12
+ functions?: {
13
+ /** Server function endpoint path. Default: "/api/fn". */
14
+ endpoint?: string;
15
+ };
16
+ /** Build plugins for the server bundle. */
17
+ plugins?: EvPlugin[];
18
+ /** Dev server options. */
19
+ dev?: {
20
+ /** API server port (dev mode). Default: 3001. */
21
+ port?: number;
22
+ /** Enable HTTPS. */
23
+ https?: boolean;
24
+ };
25
+ }
26
+ /**
27
+ * Client configuration.
28
+ *
29
+ * Controls entry point, HTML template, dev server, and transport.
30
+ */
31
+ export interface ClientConfig {
32
+ /** Client entry point. Default: "./src/main.tsx". */
33
+ entry?: string;
34
+ /** HTML template path. Default: "./index.html". */
35
+ html?: string;
36
+ /** Build plugins for the client bundle. */
37
+ plugins?: EvPlugin[];
38
+ /** Dev server options. */
39
+ dev?: {
40
+ /** Dev server port. Default: 3000. */
41
+ port?: number;
42
+ /** Enable HTTPS. */
43
+ https?: boolean;
44
+ };
45
+ }
46
+ /**
47
+ * A single loader entry.
48
+ *
49
+ * Can be a plain package name or an object with per-loader options.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * "css-loader"
54
+ * { loader: "css-loader", options: { modules: true } }
55
+ * ```
56
+ */
57
+ export type EvLoaderEntry = string | {
58
+ loader: string;
59
+ options?: Record<string, unknown>;
60
+ };
61
+ /** A loader rule declared by a plugin. */
62
+ export interface EvPluginLoader {
63
+ /** File matching pattern (e.g. /\.css$/, /\.svg$/). */
64
+ test: RegExp;
65
+ /** Pattern to exclude (e.g. /node_modules/). */
66
+ exclude?: RegExp;
67
+ /** Loader(s) to apply. */
68
+ use: EvLoaderEntry | EvLoaderEntry[];
69
+ }
70
+ /** An evjs build plugin. */
71
+ export interface EvPlugin {
72
+ /** Plugin name for debugging and logging. */
73
+ name: string;
74
+ /** Loaders to add to the build pipeline. */
75
+ loaders?: EvPluginLoader[];
76
+ }
77
+ /**
78
+ * evjs framework configuration.
79
+ */
80
+ export interface EvConfig {
81
+ server?: ServerConfig;
82
+ client?: ClientConfig;
83
+ }
84
+ /**
85
+ * Default configuration values.
86
+ *
87
+ * Single source of truth for all defaults across the framework.
88
+ */
89
+ export declare const CONFIG_DEFAULTS: {
90
+ readonly entry: "./src/main.tsx";
91
+ readonly html: "./index.html";
92
+ readonly clientPort: 3000;
93
+ readonly serverPort: 3001;
94
+ readonly endpoint: "/api/fn";
95
+ };
96
+ /**
97
+ * Define configuration for the evjs framework.
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * // ev.config.ts
102
+ * import { defineConfig } from "@evjs/cli";
103
+ *
104
+ * export default defineConfig({
105
+ * client: {
106
+ * entry: "./src/main.tsx",
107
+ * dev: { port: 3000 },
108
+ * },
109
+ * server: {
110
+ * functions: { endpoint: "/api/fn" },
111
+ * dev: { port: 3001 },
112
+ * },
113
+ * });
114
+ * ```
115
+ */
116
+ export declare function defineConfig(config: EvConfig): EvConfig;
package/dist/config.js CHANGED
@@ -24,7 +24,7 @@ export const CONFIG_DEFAULTS = {
24
24
  * dev: { port: 3000 },
25
25
  * },
26
26
  * server: {
27
- * endpoint: "/api/fn",
27
+ * functions: { endpoint: "/api/fn" },
28
28
  * dev: { port: 3001 },
29
29
  * },
30
30
  * });
@@ -0,0 +1,8 @@
1
+ import { type EvConfig } from "./config.js";
2
+ /**
3
+ * Create a webpack configuration object from EvfConfig.
4
+ *
5
+ * Returns a plain object that can be passed directly to the webpack Node API.
6
+ * No temp files are generated.
7
+ */
8
+ export declare function createWebpackConfig(config: EvConfig | undefined, cwd: string): Record<string, unknown>;
@@ -15,7 +15,7 @@ export function createWebpackConfig(config, cwd) {
15
15
  const html = client?.html ?? CONFIG_DEFAULTS.html;
16
16
  const clientPort = client?.dev?.port ?? CONFIG_DEFAULTS.clientPort;
17
17
  const serverPort = server?.dev?.port ?? CONFIG_DEFAULTS.serverPort;
18
- const endpoint = server?.endpoint ?? CONFIG_DEFAULTS.endpoint;
18
+ const endpoint = server?.functions?.endpoint ?? CONFIG_DEFAULTS.endpoint;
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");
@@ -74,8 +74,8 @@ export function createWebpackConfig(config, cwd) {
74
74
  },
75
75
  ],
76
76
  },
77
- // Plugin-declared loaders
78
- ...(client?.plugins ?? []).flatMap((plugin) => (plugin.loaders ?? []).map((rule) => {
77
+ // Plugin-declared loaders (client + server)
78
+ ...[...(client?.plugins ?? []), ...(server?.plugins ?? [])].flatMap((plugin) => (plugin.loaders ?? []).map((rule) => {
79
79
  const entries = Array.isArray(rule.use) ? rule.use : [rule.use];
80
80
  return {
81
81
  test: rule.test,
@@ -0,0 +1,23 @@
1
+ import type { EvConfig } from "./config.js";
2
+ export type { ClientConfig, EvConfig, EvLoaderEntry, EvPlugin, EvPluginLoader, ServerConfig, } from "./config.js";
3
+ export { CONFIG_DEFAULTS, defineConfig } from "./config.js";
4
+ export interface DevOptions {
5
+ cwd?: string;
6
+ }
7
+ /**
8
+ * Start the development server programmatically.
9
+ *
10
+ * @param config - evjs configuration object (from `defineConfig`)
11
+ * @param options - additional options like `cwd`
12
+ */
13
+ export declare function dev(config?: EvConfig, options?: DevOptions): Promise<void>;
14
+ export interface BuildOptions {
15
+ cwd?: string;
16
+ }
17
+ /**
18
+ * Run a production build programmatically.
19
+ *
20
+ * @param config - evjs configuration object (from `defineConfig`)
21
+ * @param options - additional options like `cwd`
22
+ */
23
+ export declare function build(config?: EvConfig, options?: BuildOptions): Promise<void>;
package/dist/index.js CHANGED
@@ -1,119 +1,96 @@
1
- #!/usr/bin/env node
2
1
  import { createRequire } from "node:module";
3
2
  import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { configure, getConsoleSink, getLogger } from "@logtape/logtape";
6
- import { Command } from "commander";
3
+ import { getLogger } from "@logtape/logtape";
7
4
  import { execa } from "execa";
8
5
  import fs from "fs-extra";
9
6
  import { CONFIG_DEFAULTS } from "./config.js";
7
+ export { CONFIG_DEFAULTS, defineConfig } from "./config.js";
10
8
  const esmRequire = createRequire(import.meta.url);
11
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
- await configure({
13
- sinks: { console: getConsoleSink() },
14
- loggers: [
15
- { category: ["logtape", "meta"], lowestLevel: "warning" },
16
- { category: ["evjs"], sinks: ["console"], lowestLevel: "info" },
17
- ],
18
- });
19
9
  const logger = getLogger(["evjs", "cli"]);
20
- const pkg = fs.readJsonSync(path.resolve(__dirname, "../package.json"));
21
- const program = new Command();
22
- program
23
- .name("ev")
24
- .description("CLI for the evjs framework")
25
- .version(pkg.version);
26
10
  /**
27
- * Load config and create webpack configuration object.
28
- *
29
- * Uses ev.config.ts when present, otherwise falls back to convention-based defaults.
30
- * No webpack.config.cjs fallback — the meta-framework owns the build config.
11
+ * Create webpack configuration from an EvConfig object.
31
12
  */
32
- async function resolveWebpackConfig(cwd) {
33
- const { loadConfig } = await import("./load-config.js");
34
- const evjsConfig = await loadConfig(cwd);
13
+ async function resolveWebpackConfig(config, cwd) {
35
14
  const { createWebpackConfig } = await import("./create-webpack-config.js");
36
- logger.info `Using ${evjsConfig ? "ev.config.ts" : "convention-based defaults"}`;
37
- const webpackConfig = createWebpackConfig(evjsConfig, cwd);
38
- return { evjsConfig, webpackConfig };
15
+ return createWebpackConfig(config, cwd);
39
16
  }
40
- program
41
- .command("dev")
42
- .description("Start development server")
43
- .action(async () => {
44
- const cwd = process.cwd();
17
+ /**
18
+ * Start the development server programmatically.
19
+ *
20
+ * @param config - evjs configuration object (from `defineConfig`)
21
+ * @param options - additional options like `cwd`
22
+ */
23
+ export async function dev(config, options) {
24
+ const cwd = options?.cwd ?? process.cwd();
45
25
  process.env.NODE_ENV ??= "development";
46
- const { evjsConfig, webpackConfig } = await resolveWebpackConfig(cwd);
47
- const serverPort = evjsConfig?.server?.dev?.port ?? CONFIG_DEFAULTS.serverPort;
26
+ const webpackConfig = await resolveWebpackConfig(config, cwd);
27
+ const serverPort = config?.server?.dev?.port ?? CONFIG_DEFAULTS.serverPort;
48
28
  logger.info `Starting development server...`;
49
- try {
50
- const webpack = esmRequire("webpack");
51
- const WebpackDevServer = esmRequire("webpack-dev-server");
52
- const compiler = webpack(webpackConfig);
53
- const devServerOptions = webpackConfig.devServer ?? {};
54
- const server = new WebpackDevServer(devServerOptions, compiler);
55
- await server.start();
56
- // Background: start Node API when server bundle is ready
57
- let apiStarted = false;
58
- compiler.hooks.done.tap("EvDevServer", async () => {
59
- if (apiStarted)
29
+ const webpack = esmRequire("webpack");
30
+ const WebpackDevServer = esmRequire("webpack-dev-server");
31
+ const compiler = webpack(webpackConfig);
32
+ const devServerOptions = webpackConfig.devServer ?? {};
33
+ const server = new WebpackDevServer(devServerOptions, compiler);
34
+ await server.start();
35
+ // Background: start Node API when server bundle is ready
36
+ let apiStarted = false;
37
+ compiler.hooks.done.tap("EvDevServer", async () => {
38
+ if (apiStarted)
39
+ return;
40
+ const manifestPath = path.resolve(cwd, "dist/manifest.json");
41
+ const bootstrapPath = path.resolve(cwd, "dist/server/_dev_start.cjs");
42
+ if (fs.existsSync(manifestPath)) {
43
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
44
+ // Start API server if there's a server entry
45
+ if (!manifest.server?.entry)
60
46
  return;
61
- const manifestPath = path.resolve(cwd, "dist/manifest.json");
62
- const bootstrapPath = path.resolve(cwd, "dist/server/_dev_start.cjs");
63
- if (fs.existsSync(manifestPath)) {
64
- const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
65
- // Start API server if there's a server entry
66
- if (!manifest.server?.entry)
67
- return;
68
- apiStarted = true;
69
- const backendConfig = evjsConfig?.server?.backend ?? "node";
70
- const [backend, ...backendExtraArgs] = backendConfig.split(/\s+/);
71
- logger.info `Server bundle detected, starting ${backend} API...`;
72
- try {
73
- const serverBundlePath = path.resolve(cwd, "dist/server", manifest.server.entry);
74
- fs.ensureDirSync(path.dirname(bootstrapPath));
75
- fs.writeFileSync(bootstrapPath, [
76
- `const bundle = require(${JSON.stringify(serverBundlePath)});`,
77
- `const app = bundle.app || bundle.createApp({ endpoint: ${JSON.stringify(evjsConfig?.server?.endpoint ?? CONFIG_DEFAULTS.endpoint)} });`,
78
- `const { serve } = require("@evjs/server/node");`,
79
- `serve(app, { port: ${serverPort} });`,
80
- ].join("\n"));
81
- // node gets --watch flags; other runtimes use their own args as-is
82
- const backendArgs = backend === "node"
83
- ? [
84
- "--watch",
85
- "--watch-preserve-output",
86
- ...backendExtraArgs,
87
- bootstrapPath,
88
- ]
89
- : [...backendExtraArgs, bootstrapPath];
90
- // Don't await execa here since it's a long-running watch process
91
- execa(backend, backendArgs, {
92
- stdio: "inherit",
93
- env: { ...process.env, NODE_ENV: "development" },
94
- }).catch(() => {
95
- apiStarted = false;
96
- });
97
- }
98
- catch (err) {
99
- logger.error `Server backend failed: ${err}`;
47
+ apiStarted = true;
48
+ const backendConfig = config?.server?.backend ?? "node";
49
+ const [backend, ...backendExtraArgs] = backendConfig.split(/\s+/);
50
+ logger.info `Server bundle detected, starting ${backend} API...`;
51
+ try {
52
+ const serverBundlePath = path.resolve(cwd, "dist/server", manifest.server.entry);
53
+ fs.ensureDirSync(path.dirname(bootstrapPath));
54
+ fs.writeFileSync(bootstrapPath, [
55
+ `const bundle = require(${JSON.stringify(serverBundlePath)});`,
56
+ `const app = bundle.app || bundle.createApp({ endpoint: ${JSON.stringify(config?.server?.functions?.endpoint ?? CONFIG_DEFAULTS.endpoint)} });`,
57
+ `const { serve } = require("@evjs/server/node");`,
58
+ `serve(app, { port: ${serverPort}, https: ${Boolean(config?.server?.dev?.https)} });`,
59
+ ].join("\n"));
60
+ // node gets --watch flags; other runtimes use their own args as-is
61
+ const backendArgs = backend === "node"
62
+ ? [
63
+ "--watch",
64
+ "--watch-preserve-output",
65
+ ...backendExtraArgs,
66
+ bootstrapPath,
67
+ ]
68
+ : [...backendExtraArgs, bootstrapPath];
69
+ // Don't await execa here since it's a long-running watch process
70
+ execa(backend, backendArgs, {
71
+ stdio: "inherit",
72
+ env: { ...process.env, NODE_ENV: "development" },
73
+ }).catch(() => {
100
74
  apiStarted = false;
101
- }
75
+ });
102
76
  }
103
- });
104
- }
105
- catch (err) {
106
- logger.error `Dev server failed to start: ${err}`;
107
- process.exit(1);
108
- }
109
- });
110
- program
111
- .command("build")
112
- .description("Build project for production")
113
- .action(async () => {
114
- const cwd = process.cwd();
77
+ catch (err) {
78
+ logger.error `Server backend failed: ${err}`;
79
+ apiStarted = false;
80
+ }
81
+ }
82
+ });
83
+ }
84
+ /**
85
+ * Run a production build programmatically.
86
+ *
87
+ * @param config - evjs configuration object (from `defineConfig`)
88
+ * @param options - additional options like `cwd`
89
+ */
90
+ export async function build(config, options) {
91
+ const cwd = options?.cwd ?? process.cwd();
115
92
  process.env.NODE_ENV ??= "production";
116
- const { webpackConfig } = await resolveWebpackConfig(cwd);
93
+ const webpackConfig = await resolveWebpackConfig(config, cwd);
117
94
  logger.info `Building for production...`;
118
95
  const webpack = esmRequire("webpack");
119
96
  const compiler = webpack(webpackConfig);
@@ -135,5 +112,4 @@ program
135
112
  });
136
113
  });
137
114
  logger.info `Build complete!`;
138
- });
139
- program.parse();
115
+ }
@@ -0,0 +1,8 @@
1
+ import type { EvConfig } from "./config.js";
2
+ /**
3
+ * Load evjs config from the project root.
4
+ *
5
+ * Looks for `ev.config.ts`, `.js`, or `.mjs` in the given directory.
6
+ * Returns undefined if no config file is found.
7
+ */
8
+ export declare function loadConfig(cwd: string): Promise<EvConfig | undefined>;
package/package.json CHANGED
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "name": "@evjs/cli",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "CLI and configuration layer for the evjs framework",
5
5
  "type": "module",
6
- "main": "./dist/config.js",
7
- "types": "./dist/config.d.ts",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "types": "./dist/config.d.ts",
11
- "import": "./dist/config.js"
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
12
  }
13
13
  },
14
14
  "publishConfig": {
15
15
  "access": "public"
16
16
  },
17
17
  "bin": {
18
- "ev": "dist/index.js"
18
+ "ev": "dist/cli.js"
19
19
  },
20
20
  "files": [
21
21
  "dist"