@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 +6 -10
- package/bin/ev.js +2 -0
- package/dist/cli.js +4 -4
- package/dist/config.d.ts +2 -119
- package/dist/config.js +2 -35
- package/dist/index.d.ts +7 -8
- package/dist/index.js +61 -80
- package/dist/load-config.d.ts +5 -2
- package/dist/load-config.js +25 -16
- package/package.json +8 -15
- package/dist/create-webpack-config.d.ts +0 -8
- package/dist/create-webpack-config.js +0 -116
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @evjs/cli
|
|
2
2
|
|
|
3
|
-
> CLI and configuration for the **@evjs/cli**
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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 `
|
|
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
|
|
92
|
+
- `@evjs/bundler-webpack`, `@evjs/build-tools`
|
package/bin/ev.js
ADDED
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2
|
-
export
|
|
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(
|
|
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(
|
|
22
|
+
export declare function build(userConfig?: EvConfig, options?: BuildOptions): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import
|
|
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
|
-
|
|
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
|
-
*
|
|
10
|
+
* Resolve the bundler adapter specified in the configuration.
|
|
12
11
|
*/
|
|
13
|
-
async function
|
|
14
|
-
const
|
|
15
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
78
|
-
|
|
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(
|
|
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
|
|
94
|
-
|
|
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
|
}
|
package/dist/load-config.d.ts
CHANGED
|
@@ -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>;
|
package/dist/load-config.js
CHANGED
|
@@ -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
|
-
*
|
|
6
|
-
*
|
|
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
|
-
|
|
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.
|
|
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": "
|
|
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
|
|
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
|
-
"
|
|
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
|
-
}
|