@evjs/cli 0.0.5 → 0.0.7

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @evjs/cli
2
2
 
3
- > CLI and configuration for the **@evjs/cli** meta-framework.
3
+ > CLI and configuration for the **@evjs/cli** fullstack framework.
4
4
 
5
5
  ## Install
6
6
 
@@ -47,21 +47,17 @@ Create `ev.config.ts` in the project root (optional):
47
47
  import { defineConfig } from "@evjs/cli";
48
48
 
49
49
  export default defineConfig({
50
- client: {
51
- entry: "./src/main.tsx",
52
- html: "./index.html",
53
- plugins: [{ name: "tailwind", module: { rules: [{ test: /\.css$/, use: ["style-loader", "css-loader", "postcss-loader"] }] } }],
54
- dev: { port: 3000 },
55
- },
50
+ entry: "./src/main.tsx",
51
+ html: "./index.html",
52
+ dev: { port: 3000 },
56
53
  server: {
57
54
  endpoint: "/api/fn",
58
-
59
55
  dev: { port: 3001 },
60
56
  },
61
57
  });
62
58
  ```
63
59
 
64
- The `client.dev` and `server.dev` fields accept extra options that are merged with defaults.
60
+ The `dev` and `server.dev` fields accept extra options that are merged with defaults.
65
61
 
66
62
  ## Project Structure
67
63
 
@@ -93,4 +89,4 @@ my-app/
93
89
  Users do NOT need to install these — they're included in `@evjs/cli`:
94
90
  - `webpack`, `webpack-dev-server`
95
91
  - `html-webpack-plugin`, `swc-loader`, `@swc/core`
96
- - `@evjs/webpack-plugin`, `@evjs/build-tools`
92
+ - `@evjs/bundler-webpack`, `@evjs/build-tools`
package/bin/ev.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "../dist/cli.js";
package/dist/cli.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  import { fileURLToPath } from "node:url";
4
5
  import { configure, getConsoleSink } from "@logtape/logtape";
5
6
  import { Command } from "commander";
6
- import fs from "fs-extra";
7
7
  import { build, dev } from "./index.js";
8
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
9
  await configure({
@@ -13,7 +13,7 @@ await configure({
13
13
  { category: ["evjs"], sinks: ["console"], lowestLevel: "info" },
14
14
  ],
15
15
  });
16
- const pkg = fs.readJsonSync(path.resolve(__dirname, "../package.json"));
16
+ const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../package.json"), "utf-8"));
17
17
  const program = new Command();
18
18
  program
19
19
  .name("ev")
@@ -25,7 +25,7 @@ program
25
25
  .action(async () => {
26
26
  const cwd = process.cwd();
27
27
  const { loadConfig } = await import("./load-config.js");
28
- const config = await loadConfig(cwd);
28
+ const config = await loadConfig(cwd, { mode: "development" });
29
29
  try {
30
30
  await dev(config ?? undefined, { cwd });
31
31
  }
@@ -40,7 +40,7 @@ program
40
40
  .action(async () => {
41
41
  const cwd = process.cwd();
42
42
  const { loadConfig } = await import("./load-config.js");
43
- const config = await loadConfig(cwd);
43
+ const config = await loadConfig(cwd, { mode: "production" });
44
44
  try {
45
45
  await build(config ?? undefined, { cwd });
46
46
  }
package/dist/config.d.ts CHANGED
@@ -1,119 +1,2 @@
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 module rule declared by a plugin. */
62
- export interface EvModuleRule {
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
- /** Plugin's module configuration. */
75
- module?: {
76
- /** Module rules to add to the build pipeline. */
77
- rules?: EvModuleRule[];
78
- };
79
- }
80
- /**
81
- * evjs framework configuration.
82
- */
83
- export interface EvConfig {
84
- server?: ServerConfig;
85
- client?: ClientConfig;
86
- }
87
- /**
88
- * Default configuration values.
89
- *
90
- * Single source of truth for all defaults across the framework.
91
- */
92
- export declare const CONFIG_DEFAULTS: {
93
- readonly entry: "./src/main.tsx";
94
- readonly html: "./index.html";
95
- readonly clientPort: 3000;
96
- readonly serverPort: 3001;
97
- readonly endpoint: "/api/fn";
98
- };
99
- /**
100
- * Define configuration for the evjs framework.
101
- *
102
- * @example
103
- * ```ts
104
- * // ev.config.ts
105
- * import { defineConfig } from "@evjs/cli";
106
- *
107
- * export default defineConfig({
108
- * client: {
109
- * entry: "./src/main.tsx",
110
- * dev: { port: 3000 },
111
- * },
112
- * server: {
113
- * functions: { endpoint: "/api/fn" },
114
- * dev: { port: 3001 },
115
- * },
116
- * });
117
- * ```
118
- */
119
- export declare function defineConfig(config: EvConfig): EvConfig;
1
+ import { CONFIG_DEFAULTS, defineConfig, type EvBundlerCtx, type EvConfig, type EvConfigCtx, type EvPlugin, type ResolvedEvConfig, resolveConfig } from "@evjs/shared";
2
+ export { type EvConfig, type EvConfigCtx, type EvBundlerCtx, type ResolvedEvConfig, type EvPlugin, CONFIG_DEFAULTS, defineConfig, resolveConfig, };
package/dist/config.js CHANGED
@@ -1,35 +1,2 @@
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
- * functions: { endpoint: "/api/fn" },
28
- * dev: { port: 3001 },
29
- * },
30
- * });
31
- * ```
32
- */
33
- export function defineConfig(config) {
34
- return config;
35
- }
1
+ import { CONFIG_DEFAULTS, defineConfig, resolveConfig, } from "@evjs/shared";
2
+ export { CONFIG_DEFAULTS, defineConfig, resolveConfig, };
package/dist/index.d.ts CHANGED
@@ -1,23 +1,22 @@
1
- import type { EvConfig } from "./config.js";
2
- export type { ClientConfig, EvConfig, EvLoaderEntry, EvModuleRule, EvPlugin, ServerConfig, } from "./config.js";
3
- export { CONFIG_DEFAULTS, defineConfig } from "./config.js";
1
+ import { CONFIG_DEFAULTS, defineConfig, type EvBundlerCtx, type EvConfig, type EvConfigCtx, type EvPlugin, type ResolvedEvConfig, resolveConfig } from "@evjs/shared";
2
+ export { CONFIG_DEFAULTS, type EvConfig, type EvBundlerCtx, type EvConfigCtx, type EvPlugin, type ResolvedEvConfig, resolveConfig, defineConfig, };
4
3
  export interface DevOptions {
5
4
  cwd?: string;
6
5
  }
6
+ export interface BuildOptions {
7
+ cwd?: string;
8
+ }
7
9
  /**
8
10
  * Start the development server programmatically.
9
11
  *
10
12
  * @param config - evjs configuration object (from `defineConfig`)
11
13
  * @param options - additional options like `cwd`
12
14
  */
13
- export declare function dev(config?: EvConfig, options?: DevOptions): Promise<void>;
14
- export interface BuildOptions {
15
- cwd?: string;
16
- }
15
+ export declare function dev(userConfig?: EvConfig, options?: DevOptions): Promise<void>;
17
16
  /**
18
17
  * Run a production build programmatically.
19
18
  *
20
19
  * @param config - evjs configuration object (from `defineConfig`)
21
20
  * @param options - additional options like `cwd`
22
21
  */
23
- export declare function build(config?: EvConfig, options?: BuildOptions): Promise<void>;
22
+ export declare function build(userConfig?: EvConfig, options?: BuildOptions): Promise<void>;
package/dist/index.js CHANGED
@@ -1,18 +1,20 @@
1
- import { createRequire } from "node:module";
1
+ import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { webpackAdapter } from "@evjs/bundler-webpack";
4
+ import { CONFIG_DEFAULTS, defineConfig, resolveConfig, } from "@evjs/shared";
3
5
  import { getLogger } from "@logtape/logtape";
4
6
  import { execa } from "execa";
5
- import fs from "fs-extra";
6
- import { CONFIG_DEFAULTS } from "./config.js";
7
- export { CONFIG_DEFAULTS, defineConfig } from "./config.js";
8
- const esmRequire = createRequire(import.meta.url);
7
+ export { CONFIG_DEFAULTS, resolveConfig, defineConfig, };
9
8
  const logger = getLogger(["evjs", "cli"]);
10
9
  /**
11
- * Create webpack configuration from an EvConfig object.
10
+ * Resolve the bundler adapter specified in the configuration.
12
11
  */
13
- async function resolveWebpackConfig(config, cwd) {
14
- const { createWebpackConfig } = await import("./create-webpack-config.js");
15
- return createWebpackConfig(config, cwd);
12
+ async function getBundlerAdapter(config) {
13
+ const bundlerName = config?.bundler?.name ?? "webpack";
14
+ if (bundlerName === "webpack") {
15
+ return webpackAdapter;
16
+ }
17
+ throw new Error(`Bundler '${bundlerName}' is not supported yet.`);
16
18
  }
17
19
  /**
18
20
  * Start the development server programmatically.
@@ -20,65 +22,63 @@ async function resolveWebpackConfig(config, cwd) {
20
22
  * @param config - evjs configuration object (from `defineConfig`)
21
23
  * @param options - additional options like `cwd`
22
24
  */
23
- export async function dev(config, options) {
25
+ export async function dev(userConfig, options) {
26
+ const config = resolveConfig(userConfig);
24
27
  const cwd = options?.cwd ?? process.cwd();
25
28
  process.env.NODE_ENV ??= "development";
26
- const webpackConfig = await resolveWebpackConfig(config, cwd);
27
- const serverPort = config?.server?.dev?.port ?? CONFIG_DEFAULTS.serverPort;
28
- logger.info `Starting development server...`;
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();
29
+ const bundler = await getBundlerAdapter(config);
35
30
  // Background: start Node API when server bundle is ready
36
31
  let apiStarted = false;
37
- compiler.hooks.done.tap("EvDevServer", async () => {
32
+ const handleServerBundleReady = () => {
38
33
  if (apiStarted)
39
34
  return;
40
35
  const manifestPath = path.resolve(cwd, "dist/manifest.json");
36
+ if (!fs.existsSync(manifestPath))
37
+ return;
38
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
39
+ if (!manifest.server?.entry)
40
+ return;
41
+ apiStarted = true;
42
+ const serverPort = config?.server?.dev?.port ?? CONFIG_DEFAULTS.serverPort;
43
+ const backendConfig = config?.server?.backend ?? "node";
44
+ const [backend, ...backendExtraArgs] = backendConfig.split(/\s+/);
45
+ logger.info `Server bundle detected, starting ${backend} API...`;
41
46
  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)
46
- return;
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(() => {
74
- apiStarted = false;
75
- });
47
+ try {
48
+ const serverBundlePath = path.resolve(cwd, "dist/server", manifest.server.entry);
49
+ if (!fs.existsSync(path.dirname(bootstrapPath))) {
50
+ fs.mkdirSync(path.dirname(bootstrapPath), { recursive: true });
76
51
  }
77
- catch (err) {
78
- logger.error `Server backend failed: ${err}`;
52
+ fs.writeFileSync(bootstrapPath, [
53
+ `const bundle = require(${JSON.stringify(serverBundlePath)});`,
54
+ `const app = bundle.app || bundle.createApp({ endpoint: ${JSON.stringify(config.server.endpoint)} });`,
55
+ `const { serve } = require("@evjs/server/node");`,
56
+ `serve(app, { port: ${serverPort}, https: ${Boolean(config.server.dev.https)} });`,
57
+ ].join("\n"));
58
+ // node gets --watch flags; other runtimes use their own args as-is
59
+ const backendArgs = backend === "node"
60
+ ? [
61
+ "--watch",
62
+ "--watch-preserve-output",
63
+ ...backendExtraArgs,
64
+ bootstrapPath,
65
+ ]
66
+ : [...backendExtraArgs, bootstrapPath];
67
+ // Don't await execa here since it's a long-running watch process
68
+ execa(backend, backendArgs, {
69
+ stdio: "inherit",
70
+ env: { ...process.env, NODE_ENV: "development" },
71
+ }).catch(() => {
79
72
  apiStarted = false;
80
- }
73
+ });
74
+ }
75
+ catch (err) {
76
+ logger.error `Server backend failed: ${err}`;
77
+ apiStarted = false;
81
78
  }
79
+ };
80
+ await bundler.dev(config, cwd, {
81
+ onServerBundleReady: handleServerBundleReady,
82
82
  });
83
83
  }
84
84
  /**
@@ -87,29 +87,10 @@ export async function dev(config, options) {
87
87
  * @param config - evjs configuration object (from `defineConfig`)
88
88
  * @param options - additional options like `cwd`
89
89
  */
90
- export async function build(config, options) {
90
+ export async function build(userConfig, options) {
91
+ const config = resolveConfig(userConfig);
91
92
  const cwd = options?.cwd ?? process.cwd();
92
93
  process.env.NODE_ENV ??= "production";
93
- const webpackConfig = await resolveWebpackConfig(config, cwd);
94
- logger.info `Building for production...`;
95
- const webpack = esmRequire("webpack");
96
- const compiler = webpack(webpackConfig);
97
- await new Promise((resolve, reject) => {
98
- compiler.run((err, stats) => {
99
- if (err) {
100
- reject(err);
101
- return;
102
- }
103
- console.log(stats.toString({
104
- colors: true,
105
- modules: false,
106
- children: true,
107
- }));
108
- if (stats.hasErrors()) {
109
- process.exit(1);
110
- }
111
- compiler.close(() => resolve());
112
- });
113
- });
114
- logger.info `Build complete!`;
94
+ const bundler = await getBundlerAdapter(config);
95
+ await bundler.build(config, cwd);
115
96
  }
@@ -1,8 +1,11 @@
1
- import type { EvConfig } from "./config.js";
1
+ import type { EvConfig, EvConfigCtx } from "./config.js";
2
2
  /**
3
3
  * Load evjs config from the project root.
4
4
  *
5
5
  * Looks for `ev.config.ts`, `.js`, or `.mjs` in the given directory.
6
6
  * Returns undefined if no config file is found.
7
+ *
8
+ * After loading, all plugin `config` hooks are executed in order.
9
+ * If a plugin injects new plugins, their hooks are also executed.
7
10
  */
8
- export declare function loadConfig(cwd: string): Promise<EvConfig | undefined>;
11
+ export declare function loadConfig(cwd: string, ctx?: EvConfigCtx): Promise<EvConfig | undefined>;
@@ -2,28 +2,20 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  const CONFIG_FILES = ["ev.config.ts", "ev.config.js", "ev.config.mjs"];
4
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.
5
+ * Historically used @swc-node/register, but it causes ERR_REQUIRE_CYCLE_MODULE inside Node 22.
6
+ * Modern evjs relies on Node's native typescript handling or built-in --loader arguments.
10
7
  */
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
- }
8
+ async function ensureTsLoader() { }
20
9
  /**
21
10
  * Load evjs config from the project root.
22
11
  *
23
12
  * Looks for `ev.config.ts`, `.js`, or `.mjs` in the given directory.
24
13
  * Returns undefined if no config file is found.
14
+ *
15
+ * After loading, all plugin `config` hooks are executed in order.
16
+ * If a plugin injects new plugins, their hooks are also executed.
25
17
  */
26
- export async function loadConfig(cwd) {
18
+ export async function loadConfig(cwd, ctx) {
27
19
  for (const filename of CONFIG_FILES) {
28
20
  const configPath = path.resolve(cwd, filename);
29
21
  if (fs.existsSync(configPath)) {
@@ -32,7 +24,24 @@ export async function loadConfig(cwd) {
32
24
  await ensureTsLoader();
33
25
  }
34
26
  const mod = await import(configPath);
35
- return mod.default ?? mod;
27
+ let config = mod.default ?? mod;
28
+ // Execute plugin config hooks
29
+ const currentCtx = ctx ?? { mode: "development" };
30
+ const executedConfigHooks = new Set();
31
+ let hasNewPlugins = true;
32
+ while (hasNewPlugins) {
33
+ hasNewPlugins = false;
34
+ const allPlugins = config.plugins ?? [];
35
+ for (const plugin of allPlugins) {
36
+ if (plugin.config && !executedConfigHooks.has(plugin.name)) {
37
+ config = plugin.config(config, currentCtx) ?? config;
38
+ executedConfigHooks.add(plugin.name);
39
+ hasNewPlugins = true;
40
+ break;
41
+ }
42
+ }
43
+ }
44
+ return config;
36
45
  }
37
46
  }
38
47
  return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evjs/cli",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "CLI and configuration layer for the evjs framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,35 +15,28 @@
15
15
  "access": "public"
16
16
  },
17
17
  "bin": {
18
- "ev": "dist/cli.js"
18
+ "ev": "bin/ev.js"
19
19
  },
20
20
  "files": [
21
- "dist"
21
+ "dist",
22
+ "bin"
22
23
  ],
23
24
  "scripts": {
24
25
  "build": "tsc",
25
26
  "dev": "tsc -w",
26
27
  "test": "vitest run",
27
- "check-types": "tsc --noEmit",
28
- "prepare": "npm run build"
28
+ "check-types": "tsc --noEmit"
29
29
  },
30
30
  "dependencies": {
31
- "@evjs/webpack-plugin": "*",
31
+ "@evjs/bundler-webpack": "*",
32
+ "@evjs/shared": "*",
32
33
  "@logtape/logtape": "^2.0.4",
33
- "@swc/core": "^1.15.21",
34
34
  "commander": "^12.1.0",
35
35
  "execa": "^9.6.1",
36
- "fs-extra": "^11.3.4",
37
36
  "glob": "^13.0.6",
38
- "html-webpack-plugin": "^5.6.6",
39
- "picocolors": "^1.1.1",
40
- "swc-loader": "^0.2.7",
41
- "webpack": "^5.105.4",
42
- "webpack-cli": "^6.0.1",
43
- "webpack-dev-server": "^5.2.3"
37
+ "picocolors": "^1.1.1"
44
38
  },
45
39
  "devDependencies": {
46
- "@types/fs-extra": "^11.0.4",
47
40
  "@types/node": "^22.19.15",
48
41
  "typescript": "^6.0.2"
49
42
  },
@@ -1,8 +0,0 @@
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>;
@@ -1,116 +0,0 @@
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?.functions?.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
23
- ? { server: { entry: server.entry } }
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
- // Plugin-declared module rules (client + server)
78
- ...[...(client?.plugins ?? []), ...(server?.plugins ?? [])].flatMap((plugin) => (plugin.module?.rules ?? []).map((rule) => {
79
- const entries = Array.isArray(rule.use) ? rule.use : [rule.use];
80
- return {
81
- test: rule.test,
82
- ...(rule.exclude ? { exclude: rule.exclude } : {}),
83
- use: entries.map((entry) => typeof entry === "string"
84
- ? { loader: resolveLoader(entry) }
85
- : {
86
- loader: resolveLoader(entry.loader),
87
- ...(entry.options ? { options: entry.options } : {}),
88
- }),
89
- };
90
- })),
91
- ],
92
- },
93
- plugins: [
94
- new HtmlWebpackPlugin({ template: html }),
95
- new EvWebpackPlugin(pluginOptions),
96
- ...(!isProduction
97
- ? [new (esmRequire("webpack").HotModuleReplacementPlugin)()]
98
- : []),
99
- ],
100
- optimization: isProduction
101
- ? { splitChunks: { chunks: "all" } }
102
- : undefined,
103
- devServer: {
104
- port: clientPort,
105
- hot: true,
106
- devMiddleware: { writeToDisk: true },
107
- proxy: [
108
- {
109
- context: [proxyBase],
110
- target: `http://localhost:${serverPort}`,
111
- },
112
- ],
113
- ...devServerOverrides,
114
- },
115
- };
116
- }