@evjs/cli 0.0.16 → 0.0.18

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
@@ -44,7 +44,7 @@ Runs webpack via Node API with `NODE_ENV=production`:
44
44
  Create `ev.config.ts` in the project root (optional):
45
45
 
46
46
  ```ts
47
- import { defineConfig } from "@evjs/cli";
47
+ import { defineConfig } from "@evjs/ev";
48
48
 
49
49
  export default defineConfig({
50
50
  entry: "./src/main.tsx",
@@ -82,7 +82,7 @@ my-app/
82
82
  1. **Don't create `webpack.config.cjs`** — use `ev.config.ts` instead
83
83
  2. **Don't install webpack manually** — it's a dependency of `@evjs/cli`
84
84
  3. **Config file must be `ev.config.ts`** — not `evjs.config.ts`
85
- 4. **Import `defineConfig` from `@evjs/cli`** — not from `@evjs/server`
85
+ 4. **Import `defineConfig` from `@evjs/ev`** — not from `@evjs/server`
86
86
 
87
87
  ## Bundled Dependencies
88
88
 
package/dist/cli.js CHANGED
@@ -26,7 +26,7 @@ program
26
26
  .action(async () => {
27
27
  const cwd = process.cwd();
28
28
  const { loadConfig } = await import("./load-config.js");
29
- const config = await loadConfig(cwd, { mode: "development" });
29
+ const config = await loadConfig(cwd);
30
30
  try {
31
31
  await dev(config ?? undefined, { cwd });
32
32
  }
@@ -41,7 +41,7 @@ program
41
41
  .action(async () => {
42
42
  const cwd = process.cwd();
43
43
  const { loadConfig } = await import("./load-config.js");
44
- const config = await loadConfig(cwd, { mode: "production" });
44
+ const config = await loadConfig(cwd);
45
45
  try {
46
46
  await build(config ?? undefined, { cwd });
47
47
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
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, };
1
+ import { CONFIG_DEFAULTS, defineConfig, type EvBuildResult, type EvBundlerCtx, type EvConfig, type EvPlugin, type EvPluginContext, type EvPluginHooks, type ResolvedEvConfig, resolveConfig } from "@evjs/ev";
2
+ export { CONFIG_DEFAULTS, type EvConfig, type EvBuildResult, type EvBundlerCtx, type EvPlugin, type EvPluginContext, type EvPluginHooks, type ResolvedEvConfig, resolveConfig, defineConfig, };
3
3
  export interface DevOptions {
4
4
  cwd?: string;
5
5
  }
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { webpackAdapter } from "@evjs/bundler-webpack";
4
- import { CONFIG_DEFAULTS, defineConfig, resolveConfig, } from "@evjs/shared";
4
+ import { CONFIG_DEFAULTS, defineConfig, resolveConfig, } from "@evjs/ev";
5
5
  import { getLogger } from "@logtape/logtape";
6
6
  import { execa } from "execa";
7
7
  export { CONFIG_DEFAULTS, resolveConfig, defineConfig, };
@@ -16,6 +16,60 @@ async function getBundlerAdapter(config) {
16
16
  }
17
17
  throw new Error(`Bundler '${bundlerName}' is not supported yet.`);
18
18
  }
19
+ /**
20
+ * Run plugin setup() hooks and collect lifecycle hooks.
21
+ */
22
+ async function collectPluginHooks(plugins, ctx) {
23
+ const allHooks = [];
24
+ for (const plugin of plugins) {
25
+ if (plugin.setup) {
26
+ const hooks = await plugin.setup(ctx);
27
+ if (hooks) {
28
+ allHooks.push(hooks);
29
+ }
30
+ }
31
+ }
32
+ return allHooks;
33
+ }
34
+ /**
35
+ * Run all buildStart hooks sequentially.
36
+ */
37
+ async function runBuildStartHooks(hooks) {
38
+ for (const h of hooks) {
39
+ if (h.buildStart) {
40
+ await h.buildStart();
41
+ }
42
+ }
43
+ }
44
+ /**
45
+ * Run all buildEnd hooks sequentially.
46
+ */
47
+ async function runBuildEndHooks(hooks, result) {
48
+ for (const h of hooks) {
49
+ if (h.buildEnd) {
50
+ await h.buildEnd(result);
51
+ }
52
+ }
53
+ }
54
+ /**
55
+ * Read build manifests from disk.
56
+ */
57
+ function readBuildResult(cwd, serverEnabled, isRebuild) {
58
+ const clientManifestPath = serverEnabled
59
+ ? path.resolve(cwd, "dist/client/manifest.json")
60
+ : path.resolve(cwd, "dist/manifest.json");
61
+ if (!fs.existsSync(clientManifestPath))
62
+ return null;
63
+ const clientManifest = JSON.parse(fs.readFileSync(clientManifestPath, "utf-8"));
64
+ let serverManifest;
65
+ if (serverEnabled) {
66
+ const serverManifestPath = path.resolve(cwd, "dist/server/manifest.json");
67
+ if (fs.existsSync(serverManifestPath)) {
68
+ serverManifest = JSON.parse(fs.readFileSync(serverManifestPath, "utf-8"));
69
+ }
70
+ }
71
+ return { clientManifest, serverManifest, isRebuild };
72
+ }
19
73
  /**
20
74
  * Start the development server programmatically.
21
75
  *
@@ -26,10 +80,15 @@ export async function dev(userConfig, options) {
26
80
  const config = resolveConfig(userConfig);
27
81
  const cwd = options?.cwd ?? process.cwd();
28
82
  process.env.NODE_ENV ??= "development";
83
+ // Collect plugin hooks
84
+ const pluginCtx = { mode: "development", config };
85
+ const hooks = await collectPluginHooks(config.plugins, pluginCtx);
86
+ // Run buildStart hooks
87
+ await runBuildStartHooks(hooks);
29
88
  const bundler = await getBundlerAdapter(config);
30
89
  // Track the running API server process for lifecycle management.
31
- // Using a reference instead of a boolean allows proper restart on crash.
32
90
  let apiProcess = null;
91
+ let isFirstBuild = true;
33
92
  const handleServerBundleReady = () => {
34
93
  if (!config.serverEnabled)
35
94
  return;
@@ -43,6 +102,14 @@ export async function dev(userConfig, options) {
43
102
  }
44
103
  if (!manifest.entry)
45
104
  return;
105
+ // Run buildEnd hooks with manifests
106
+ const buildResult = readBuildResult(cwd, config.serverEnabled, !isFirstBuild);
107
+ if (buildResult) {
108
+ runBuildEndHooks(hooks, buildResult).catch((err) => {
109
+ logger.error `Plugin buildEnd hook failed: ${err}`;
110
+ });
111
+ }
112
+ isFirstBuild = false;
46
113
  // Kill previous process before restarting (handles both first start and restarts)
47
114
  if (apiProcess) {
48
115
  logger.info `Restarting API server...`;
@@ -92,9 +159,7 @@ export async function dev(userConfig, options) {
92
159
  apiProcess = null;
93
160
  }
94
161
  };
95
- await bundler.dev(config, cwd, {
96
- onServerBundleReady: handleServerBundleReady,
97
- });
162
+ await bundler.dev(config, cwd, { onServerBundleReady: handleServerBundleReady }, hooks);
98
163
  }
99
164
  /**
100
165
  * Run a production build programmatically.
@@ -106,6 +171,16 @@ export async function build(userConfig, options) {
106
171
  const config = resolveConfig(userConfig);
107
172
  const cwd = options?.cwd ?? process.cwd();
108
173
  process.env.NODE_ENV ??= "production";
174
+ // Collect plugin hooks
175
+ const pluginCtx = { mode: "production", config };
176
+ const hooks = await collectPluginHooks(config.plugins, pluginCtx);
177
+ // Run buildStart hooks
178
+ await runBuildStartHooks(hooks);
109
179
  const bundler = await getBundlerAdapter(config);
110
- await bundler.build(config, cwd);
180
+ await bundler.build(config, cwd, hooks);
181
+ // Run buildEnd hooks with manifests
182
+ const buildResult = readBuildResult(cwd, config.serverEnabled, false);
183
+ if (buildResult) {
184
+ await runBuildEndHooks(hooks, buildResult);
185
+ }
111
186
  }
@@ -1,11 +1,8 @@
1
- import type { EvConfig, EvConfigCtx } from "@evjs/shared";
1
+ import type { EvConfig } from "@evjs/ev";
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.
10
7
  */
11
- export declare function loadConfig(cwd: string, ctx?: EvConfigCtx): Promise<EvConfig | undefined>;
8
+ export declare function loadConfig(cwd: string): Promise<EvConfig | undefined>;
@@ -6,33 +6,13 @@ const CONFIG_FILES = ["ev.config.ts", "ev.config.js", "ev.config.mjs"];
6
6
  *
7
7
  * Looks for `ev.config.ts`, `.js`, or `.mjs` in the given directory.
8
8
  * Returns undefined if no config file is found.
9
- *
10
- * After loading, all plugin `config` hooks are executed in order.
11
- * If a plugin injects new plugins, their hooks are also executed.
12
9
  */
13
- export async function loadConfig(cwd, ctx) {
10
+ export async function loadConfig(cwd) {
14
11
  for (const filename of CONFIG_FILES) {
15
12
  const configPath = path.resolve(cwd, filename);
16
13
  if (fs.existsSync(configPath)) {
17
14
  const mod = await import(configPath);
18
- let config = mod.default ?? mod;
19
- // Execute plugin config hooks
20
- const currentCtx = ctx ?? { mode: "development" };
21
- const executedConfigHooks = new Set();
22
- let hasNewPlugins = true;
23
- while (hasNewPlugins) {
24
- hasNewPlugins = false;
25
- const allPlugins = config.plugins ?? [];
26
- for (const plugin of allPlugins) {
27
- if (plugin.config && !executedConfigHooks.has(plugin.name)) {
28
- config = plugin.config(config, currentCtx) ?? config;
29
- executedConfigHooks.add(plugin.name);
30
- hasNewPlugins = true;
31
- break;
32
- }
33
- }
34
- }
35
- return config;
15
+ return mod.default ?? mod;
36
16
  }
37
17
  }
38
18
  return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evjs/cli",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "CLI and configuration layer for the evjs framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -29,12 +29,10 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@evjs/bundler-webpack": "*",
32
- "@evjs/shared": "*",
32
+ "@evjs/ev": "*",
33
33
  "@logtape/logtape": "^2.0.4",
34
34
  "commander": "^12.1.0",
35
- "execa": "^9.6.1",
36
- "glob": "^13.0.6",
37
- "picocolors": "^1.1.1"
35
+ "execa": "^9.6.1"
38
36
  },
39
37
  "devDependencies": {
40
38
  "@types/node": "^22.19.15",