@eggjs/bin 8.0.2-beta.1 → 8.0.2-beta.10

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
@@ -36,6 +36,7 @@ Add `egg-bin` to `package.json` scripts:
36
36
  "test-local": "egg-bin test",
37
37
  "test": "npm run lint -- --fix && npm run test-local",
38
38
  "cov": "egg-bin cov",
39
+ "bundle": "egg-bin bundle",
39
40
  "lint": "eslint .",
40
41
  "ci": "npm run lint && npm run cov"
41
42
  }
@@ -183,6 +184,57 @@ You can set `COV_EXCLUDES` env to add glob patterns to exclude from coverage (co
183
184
  COV_EXCLUDES="app/plugins/c*,app/autocreate/**" egg-bin cov
184
185
  ```
185
186
 
187
+ ### bundle
188
+
189
+ Bundle an Egg application into a deployable artifact with `@eggjs/egg-bundler`.
190
+
191
+ ```bash
192
+ egg-bin bundle
193
+ egg-bin bundle --output ./dist-bundle
194
+ egg-bin bundle --mode development
195
+ egg-bin bundle --framework egg --output ./out
196
+ egg-bin bundle --pack-alias some-package=./node_modules/some-package/index.js
197
+ ```
198
+
199
+ The command writes the bundle to `./dist-bundle` by default. The generated
200
+ artifact can be started with Node from the output directory:
201
+
202
+ ```bash
203
+ cd dist-bundle
204
+ node worker.js
205
+ ```
206
+
207
+ #### bundle options
208
+
209
+ - `--output` / `-o` output directory, default to `./dist-bundle`
210
+ - `--manifest` path to `manifest.json`, default to `<baseDir>/.egg/manifest.json`
211
+ - `--framework` / `-f` framework package specifier, defaulting to
212
+ `package.json#egg.framework` or `egg`. Absolute framework paths are not
213
+ supported by the bundled runtime.
214
+ - `--mode` build mode, `production` or `development`, default to `production`
215
+ - `--force-external` package name to always keep external, supports multiple
216
+ - `--inline-external` package name to force inline, supports multiple
217
+ - `--pack-alias` `@utoo/pack` resolve alias in `<specifier>=<target>` form,
218
+ supports multiple. Dot-relative targets such as `./target.js` and
219
+ `../target.js` are resolved from the application base directory
220
+
221
+ Bundle aliases can also be committed in the application `module.yml`:
222
+
223
+ ```yaml
224
+ bundle:
225
+ pack:
226
+ resolve:
227
+ alias:
228
+ some-package: ./node_modules/some-package/index.js
229
+ ```
230
+
231
+ `module.yml` aliases use the same target resolution rules as `--pack-alias`.
232
+ If the same alias is configured in both places, the explicit CLI
233
+ `--pack-alias` value wins.
234
+
235
+ See [`@eggjs/egg-bundler`](../egg-bundler/README.md) for the programmatic API
236
+ and output structure.
237
+
186
238
  ## Breaking Changes (v8)
187
239
 
188
240
  ### Migrated from Mocha to Vitest
@@ -32,7 +32,6 @@ declare abstract class BaseCommand<T extends typeof Command> extends Command {
32
32
  protected args: Args$1<T>;
33
33
  protected env: {
34
34
  [key: string]: string | undefined;
35
- TZ?: string | undefined;
36
35
  };
37
36
  protected pkg: Record<string, any>;
38
37
  protected isESM: boolean;
@@ -46,6 +45,7 @@ declare abstract class BaseCommand<T extends typeof Command> extends Command {
46
45
  protected formatRequires(): Promise<string[]>;
47
46
  protected formatImportModule(modulePath: string): string;
48
47
  protected addNodeOptions(options: string): void;
48
+ protected buildRequiresExecArgv(): Promise<string[]>;
49
49
  protected forkNode(modulePath: string, forkArgs: string[], options?: ForkNodeOptions): Promise<void>;
50
50
  }
51
51
  //#endregion
@@ -1,10 +1,10 @@
1
1
  import { getSourceDirname, hasTsConfig, readPackageJSON } from "./utils.js";
2
+ import { createRequire } from "node:module";
2
3
  import { fork } from "node:child_process";
3
4
  import os from "node:os";
4
5
  import path from "node:path";
5
6
  import { pathToFileURL } from "node:url";
6
7
  import { debuglog } from "node:util";
7
- import { importResolve } from "@eggjs/utils";
8
8
  import { Command, Flags, Interfaces } from "@oclif/core";
9
9
 
10
10
  //#region src/baseCommand.ts
@@ -162,20 +162,26 @@ var BaseCommand = class extends Command {
162
162
  let rootDir = path.dirname(getSourceDirname());
163
163
  if (path.basename(rootDir) === "dist") rootDir = path.dirname(rootDir);
164
164
  const findPaths = [flags.base, rootDir];
165
+ const cjsResolve = (specifier) => {
166
+ for (const p of findPaths) try {
167
+ return createRequire(path.join(p, "package.json")).resolve(specifier);
168
+ } catch {}
169
+ throw new Error(`Cannot resolve '${specifier}' from ${findPaths.join(", ")}`);
170
+ };
165
171
  this.isESM = pkg.type === "module";
166
172
  if (typescript) {
167
173
  flags.tscompiler = flags.tscompiler ?? "ts-node/register";
168
- const tsNodeRegister = importResolve(flags.tscompiler, { paths: findPaths });
174
+ const tsNodeRegister = cjsResolve(flags.tscompiler);
169
175
  flags.tscompiler = tsNodeRegister;
170
176
  this.addNodeOptions(this.formatImportModule(tsNodeRegister));
171
177
  this.env.EGG_TYPESCRIPT = "true";
172
178
  process.env.EGG_TYPESCRIPT = "true";
173
179
  this.env.TS_NODE_FILES = process.env.TS_NODE_FILES ?? "true";
174
- const tsConfigPathsRegister = importResolve("tsconfig-paths/register", { paths: findPaths });
180
+ const tsConfigPathsRegister = cjsResolve("tsconfig-paths/register");
175
181
  this.addNodeOptions(this.formatImportModule(tsConfigPathsRegister));
176
182
  }
177
183
  if (this.isESM) {
178
- let esmLoader = importResolve("ts-node/esm", { paths: findPaths });
184
+ let esmLoader = cjsResolve("ts-node/esm");
179
185
  esmLoader = pathToFileURL(esmLoader).href;
180
186
  this.addNodeOptions("--no-warnings");
181
187
  this.addNodeOptions(`--loader ${esmLoader}`);
@@ -235,6 +241,16 @@ var BaseCommand = class extends Command {
235
241
  if (!this.env.NODE_OPTIONS.includes(options)) this.env.NODE_OPTIONS = `${this.env.NODE_OPTIONS} ${options}`;
236
242
  } else this.env.NODE_OPTIONS = options;
237
243
  }
244
+ async buildRequiresExecArgv() {
245
+ const requires = await this.formatRequires();
246
+ const execArgv = [];
247
+ for (const r of requires) {
248
+ const module = this.formatImportModule(r);
249
+ const splitIndex = module.indexOf(" ");
250
+ if (splitIndex !== -1) execArgv.push(module.slice(0, splitIndex), module.slice(splitIndex + 2, -1));
251
+ }
252
+ return execArgv;
253
+ }
238
254
  async forkNode(modulePath, forkArgs, options = {}) {
239
255
  const env = {
240
256
  ...this.env,
@@ -0,0 +1,20 @@
1
+ import { BaseCommand } from "../baseCommand.js";
2
+ import * as _oclif_core_interfaces18 from "@oclif/core/interfaces";
3
+
4
+ //#region src/commands/bundle.d.ts
5
+ declare class Bundle extends BaseCommand<typeof Bundle> {
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ output: _oclif_core_interfaces18.OptionFlag<string, _oclif_core_interfaces18.CustomOptions>;
10
+ manifest: _oclif_core_interfaces18.OptionFlag<string | undefined, _oclif_core_interfaces18.CustomOptions>;
11
+ framework: _oclif_core_interfaces18.OptionFlag<string | undefined, _oclif_core_interfaces18.CustomOptions>;
12
+ mode: _oclif_core_interfaces18.OptionFlag<string, _oclif_core_interfaces18.CustomOptions>;
13
+ 'force-external': _oclif_core_interfaces18.OptionFlag<string[], _oclif_core_interfaces18.CustomOptions>;
14
+ 'inline-external': _oclif_core_interfaces18.OptionFlag<string[], _oclif_core_interfaces18.CustomOptions>;
15
+ 'pack-alias': _oclif_core_interfaces18.OptionFlag<string[], _oclif_core_interfaces18.CustomOptions>;
16
+ };
17
+ run(): Promise<void>;
18
+ }
19
+ //#endregion
20
+ export { Bundle as default };
@@ -0,0 +1,99 @@
1
+ import { BaseCommand } from "../baseCommand.js";
2
+ import path from "node:path";
3
+ import { debuglog } from "node:util";
4
+ import { Flags } from "@oclif/core";
5
+ import fs from "node:fs/promises";
6
+
7
+ //#region src/commands/bundle.ts
8
+ const debug = debuglog("egg/bin/commands/bundle");
9
+ const bundleModes = ["production", "development"];
10
+ function getBundleMode(mode) {
11
+ if (mode === "production" || mode === "development") return mode;
12
+ throw new Error(`Unsupported bundle mode: ${mode}`);
13
+ }
14
+ function parsePackAliases(values, baseDir) {
15
+ if (values.length === 0) return void 0;
16
+ const alias = {};
17
+ for (const value of values) {
18
+ const separator = value.indexOf("=");
19
+ if (separator <= 0 || separator === value.length - 1) throw new Error(`Invalid --pack-alias value: ${value}. Expected <specifier>=<target>.`);
20
+ const specifier = value.slice(0, separator);
21
+ const target = value.slice(separator + 1);
22
+ alias[specifier] = target.startsWith(".") ? path.resolve(baseDir, target) : target;
23
+ }
24
+ return alias;
25
+ }
26
+ async function getBundleFrameworkSpecifier(baseDir, framework) {
27
+ if (framework) return framework;
28
+ const pkgPath = path.join(baseDir, "package.json");
29
+ const pkg = JSON.parse(await fs.readFile(pkgPath, "utf8"));
30
+ return typeof pkg.egg?.framework === "string" && pkg.egg.framework ? pkg.egg.framework : "egg";
31
+ }
32
+ var Bundle = class extends BaseCommand {
33
+ static description = "Bundle an egg app into a deployable artifact using @eggjs/egg-bundler";
34
+ static examples = [
35
+ "<%= config.bin %> <%= command.id %>",
36
+ "<%= config.bin %> <%= command.id %> --output ./dist-bundle",
37
+ "<%= config.bin %> <%= command.id %> --mode development",
38
+ "<%= config.bin %> <%= command.id %> --framework egg --output ./out",
39
+ "<%= config.bin %> <%= command.id %> --pack-alias some-package=./node_modules/some-package/index.js"
40
+ ];
41
+ static flags = {
42
+ output: Flags.string({
43
+ char: "o",
44
+ description: "output directory for the bundled artifact",
45
+ default: "./dist-bundle"
46
+ }),
47
+ manifest: Flags.string({ description: "path to manifest.json (defaults to <baseDir>/.egg/manifest.json)" }),
48
+ framework: Flags.string({
49
+ char: "f",
50
+ description: "framework package specifier"
51
+ }),
52
+ mode: Flags.string({
53
+ description: "build mode",
54
+ options: [...bundleModes],
55
+ default: "production"
56
+ }),
57
+ "force-external": Flags.string({
58
+ description: "package name to always mark as external (repeatable)",
59
+ multiple: true,
60
+ default: []
61
+ }),
62
+ "inline-external": Flags.string({
63
+ description: "package name to force-inline even if auto-detected as external (repeatable)",
64
+ multiple: true,
65
+ default: []
66
+ }),
67
+ "pack-alias": Flags.string({
68
+ description: "@utoo/pack resolve alias in <specifier>=<target> form, dot-relative targets resolve from --base",
69
+ multiple: true,
70
+ default: []
71
+ })
72
+ };
73
+ async run() {
74
+ const { flags } = this;
75
+ const baseDir = flags.base;
76
+ const outputDir = path.isAbsolute(flags.output) ? flags.output : path.join(baseDir, flags.output);
77
+ const manifestPath = flags.manifest ? path.isAbsolute(flags.manifest) ? flags.manifest : path.join(baseDir, flags.manifest) : void 0;
78
+ debug("bundle: baseDir=%s, outputDir=%s, framework=%s, mode=%s", baseDir, outputDir, flags.framework, flags.mode);
79
+ const { bundle } = await import("@eggjs/egg-bundler");
80
+ const packAlias = parsePackAliases(flags["pack-alias"], baseDir);
81
+ const result = await bundle({
82
+ baseDir,
83
+ outputDir,
84
+ manifestPath,
85
+ framework: await getBundleFrameworkSpecifier(baseDir, flags.framework),
86
+ mode: getBundleMode(flags.mode),
87
+ externals: {
88
+ force: flags["force-external"],
89
+ inline: flags["inline-external"]
90
+ },
91
+ ...packAlias ? { pack: { resolve: { alias: packAlias } } } : {}
92
+ });
93
+ this.log(`bundled to ${result.outputDir} (${result.files.length} files)`);
94
+ this.log(`manifest: ${result.manifestPath}`);
95
+ }
96
+ };
97
+
98
+ //#endregion
99
+ export { Bundle as default };
@@ -14,6 +14,7 @@ declare class Cov<T extends typeof Cov> extends Test<T> {
14
14
  grep: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
15
15
  changed: _oclif_core_interfaces0.BooleanFlag<boolean>;
16
16
  watch: _oclif_core_interfaces0.BooleanFlag<boolean>;
17
+ pool: _oclif_core_interfaces0.OptionFlag<string, _oclif_core_interfaces0.CustomOptions>;
17
18
  };
18
19
  protected get defaultCoverageExcludes(): string[];
19
20
  /**
@@ -1,15 +1,15 @@
1
1
  import { BaseCommand } from "../baseCommand.js";
2
- import * as _oclif_core_interfaces9 from "@oclif/core/interfaces";
2
+ import * as _oclif_core_interfaces32 from "@oclif/core/interfaces";
3
3
 
4
4
  //#region src/commands/dev.d.ts
5
5
  declare class Dev<T extends typeof Dev> extends BaseCommand<T> {
6
6
  static description: string;
7
7
  static examples: string[];
8
8
  static flags: {
9
- port: _oclif_core_interfaces9.OptionFlag<number | undefined, _oclif_core_interfaces9.CustomOptions>;
10
- workers: _oclif_core_interfaces9.OptionFlag<number, _oclif_core_interfaces9.CustomOptions>;
11
- framework: _oclif_core_interfaces9.OptionFlag<string | undefined, _oclif_core_interfaces9.CustomOptions>;
12
- sticky: _oclif_core_interfaces9.BooleanFlag<boolean>;
9
+ port: _oclif_core_interfaces32.OptionFlag<number | undefined, _oclif_core_interfaces32.CustomOptions>;
10
+ workers: _oclif_core_interfaces32.OptionFlag<number, _oclif_core_interfaces32.CustomOptions>;
11
+ framework: _oclif_core_interfaces32.OptionFlag<string | undefined, _oclif_core_interfaces32.CustomOptions>;
12
+ sticky: _oclif_core_interfaces32.BooleanFlag<boolean>;
13
13
  };
14
14
  run(): Promise<void>;
15
15
  protected formatEggStartOptions(): Promise<{
@@ -1,8 +1,8 @@
1
1
  import { getSourceFilename } from "../utils.js";
2
2
  import { BaseCommand } from "../baseCommand.js";
3
3
  import { debuglog } from "node:util";
4
- import { getFrameworkPath } from "@eggjs/utils";
5
4
  import { Flags } from "@oclif/core";
5
+ import { getFrameworkPath } from "@eggjs/utils";
6
6
  import { detect } from "detect-port";
7
7
 
8
8
  //#region src/commands/dev.ts
@@ -31,13 +31,7 @@ var Dev = class extends BaseCommand {
31
31
  const serverBin = getSourceFilename(`../scripts/start-cluster.${this.isESM ? "mjs" : "cjs"}`);
32
32
  const eggStartOptions = await this.formatEggStartOptions();
33
33
  const args = [JSON.stringify(eggStartOptions)];
34
- const requires = await this.formatRequires();
35
- const execArgv = [];
36
- for (const r of requires) {
37
- const module = this.formatImportModule(r);
38
- const splitIndex = module.indexOf(" ");
39
- if (splitIndex !== -1) execArgv.push(module.slice(0, splitIndex), module.slice(splitIndex + 2, -1));
40
- }
34
+ const execArgv = await this.buildRequiresExecArgv();
41
35
  await this.forkNode(serverBin, args, { execArgv });
42
36
  }
43
37
  async formatEggStartOptions() {
@@ -0,0 +1,22 @@
1
+ import { BaseCommand } from "../baseCommand.js";
2
+ import * as _oclif_core_interfaces11 from "@oclif/core/interfaces";
3
+
4
+ //#region src/commands/manifest.d.ts
5
+ declare class Manifest<T extends typeof Manifest> extends BaseCommand<T> {
6
+ static description: string;
7
+ static examples: string[];
8
+ static args: {
9
+ action: _oclif_core_interfaces11.Arg<string, Record<string, unknown>>;
10
+ };
11
+ static flags: {
12
+ framework: _oclif_core_interfaces11.OptionFlag<string | undefined, _oclif_core_interfaces11.CustomOptions>;
13
+ env: _oclif_core_interfaces11.OptionFlag<string, _oclif_core_interfaces11.CustomOptions>;
14
+ scope: _oclif_core_interfaces11.OptionFlag<string, _oclif_core_interfaces11.CustomOptions>;
15
+ };
16
+ run(): Promise<void>;
17
+ private runGenerate;
18
+ private runValidate;
19
+ private runClean;
20
+ }
21
+ //#endregion
22
+ export { Manifest as default };
@@ -0,0 +1,108 @@
1
+ import { getSourceFilename } from "../utils.js";
2
+ import { BaseCommand } from "../baseCommand.js";
3
+ import { debuglog } from "node:util";
4
+ import { Args, Flags } from "@oclif/core";
5
+ import { getFrameworkPath } from "@eggjs/utils";
6
+
7
+ //#region src/commands/manifest.ts
8
+ const debug = debuglog("egg/bin/commands/manifest");
9
+ var Manifest = class extends BaseCommand {
10
+ static description = "Manage the startup manifest for faster cold starts";
11
+ static examples = [
12
+ "<%= config.bin %> <%= command.id %> generate",
13
+ "<%= config.bin %> <%= command.id %> generate --env=prod",
14
+ "<%= config.bin %> <%= command.id %> validate --env=prod",
15
+ "<%= config.bin %> <%= command.id %> clean"
16
+ ];
17
+ static args = { action: Args.string({
18
+ required: true,
19
+ description: "Action to perform",
20
+ options: [
21
+ "generate",
22
+ "validate",
23
+ "clean"
24
+ ]
25
+ }) };
26
+ static flags = {
27
+ framework: Flags.string({ description: "specify framework that can be absolute path or npm package" }),
28
+ env: Flags.string({
29
+ description: "server environment for manifest generation/validation",
30
+ default: "prod"
31
+ }),
32
+ scope: Flags.string({
33
+ description: "server scope for manifest validation",
34
+ default: ""
35
+ })
36
+ };
37
+ async run() {
38
+ const { action } = this.args;
39
+ switch (action) {
40
+ case "generate":
41
+ await this.runGenerate();
42
+ break;
43
+ case "validate":
44
+ await this.runValidate();
45
+ break;
46
+ case "clean":
47
+ await this.runClean();
48
+ break;
49
+ }
50
+ }
51
+ async runGenerate() {
52
+ const { flags } = this;
53
+ const framework = getFrameworkPath({
54
+ framework: flags.framework,
55
+ baseDir: flags.base
56
+ });
57
+ debug("generate manifest: baseDir=%s, framework=%s, env=%s, scope=%s", flags.base, framework, flags.env, flags.scope);
58
+ const options = {
59
+ baseDir: flags.base,
60
+ framework,
61
+ env: flags.env,
62
+ scope: flags.scope
63
+ };
64
+ const serverBin = getSourceFilename("../scripts/manifest-generate.mjs");
65
+ const args = [JSON.stringify(options)];
66
+ const execArgv = await this.buildRequiresExecArgv();
67
+ await this.forkNode(serverBin, args, { execArgv });
68
+ }
69
+ async runValidate() {
70
+ const { flags } = this;
71
+ debug("validate manifest: baseDir=%s, env=%s, scope=%s", flags.base, flags.env, flags.scope);
72
+ const savedEggManifest = process.env.EGG_MANIFEST;
73
+ process.env.EGG_MANIFEST = "true";
74
+ try {
75
+ const { ManifestStore } = await import("@eggjs/core");
76
+ const store = ManifestStore.load(flags.base, flags.env, flags.scope);
77
+ if (!store) {
78
+ console.error("[manifest] Manifest is invalid or does not exist");
79
+ return this.exit(1);
80
+ }
81
+ const { data } = store;
82
+ const resolveCacheCount = Object.keys(data.resolveCache ?? {}).length;
83
+ const fileDiscoveryCount = Object.keys(data.fileDiscovery ?? {}).length;
84
+ const extensionCount = Object.keys(data.extensions ?? {}).length;
85
+ console.log("[manifest] Manifest is valid");
86
+ console.log("[manifest] version: %d", data.version);
87
+ console.log("[manifest] generatedAt: %s", data.generatedAt);
88
+ console.log("[manifest] serverEnv: %s", data.invalidation.serverEnv);
89
+ console.log("[manifest] serverScope: %s", data.invalidation.serverScope);
90
+ console.log("[manifest] resolveCache entries: %d", resolveCacheCount);
91
+ console.log("[manifest] fileDiscovery entries: %d", fileDiscoveryCount);
92
+ console.log("[manifest] extension entries: %d", extensionCount);
93
+ } finally {
94
+ if (savedEggManifest === void 0) delete process.env.EGG_MANIFEST;
95
+ else process.env.EGG_MANIFEST = savedEggManifest;
96
+ }
97
+ }
98
+ async runClean() {
99
+ const { flags } = this;
100
+ debug("clean manifest: baseDir=%s", flags.base);
101
+ const { ManifestStore } = await import("@eggjs/core");
102
+ ManifestStore.clean(flags.base);
103
+ console.log("[manifest] Manifest cleaned");
104
+ }
105
+ };
106
+
107
+ //#endregion
108
+ export { Manifest as default };
@@ -1,21 +1,22 @@
1
1
  import { BaseCommand } from "../baseCommand.js";
2
2
  import { InlineConfig } from "vitest/node";
3
- import * as _oclif_core_interfaces16 from "@oclif/core/interfaces";
3
+ import * as _oclif_core_interfaces39 from "@oclif/core/interfaces";
4
4
 
5
5
  //#region src/commands/test.d.ts
6
6
  declare class Test<T extends typeof Test> extends BaseCommand<T> {
7
7
  static args: {
8
- file: _oclif_core_interfaces16.Arg<string | undefined, Record<string, unknown>>;
8
+ file: _oclif_core_interfaces39.Arg<string | undefined, Record<string, unknown>>;
9
9
  };
10
10
  static description: string;
11
11
  static examples: string[];
12
12
  static flags: {
13
- bail: _oclif_core_interfaces16.BooleanFlag<boolean>;
14
- timeout: _oclif_core_interfaces16.OptionFlag<number, _oclif_core_interfaces16.CustomOptions>;
15
- 'no-timeout': _oclif_core_interfaces16.BooleanFlag<boolean>;
16
- grep: _oclif_core_interfaces16.OptionFlag<string | undefined, _oclif_core_interfaces16.CustomOptions>;
17
- changed: _oclif_core_interfaces16.BooleanFlag<boolean>;
18
- watch: _oclif_core_interfaces16.BooleanFlag<boolean>;
13
+ bail: _oclif_core_interfaces39.BooleanFlag<boolean>;
14
+ timeout: _oclif_core_interfaces39.OptionFlag<number, _oclif_core_interfaces39.CustomOptions>;
15
+ 'no-timeout': _oclif_core_interfaces39.BooleanFlag<boolean>;
16
+ grep: _oclif_core_interfaces39.OptionFlag<string | undefined, _oclif_core_interfaces39.CustomOptions>;
17
+ changed: _oclif_core_interfaces39.BooleanFlag<boolean>;
18
+ watch: _oclif_core_interfaces39.BooleanFlag<boolean>;
19
+ pool: _oclif_core_interfaces39.OptionFlag<string, _oclif_core_interfaces39.CustomOptions>;
19
20
  };
20
21
  run(): Promise<void>;
21
22
  protected buildVitestConfig(files: string[]): Promise<InlineConfig>;
@@ -1,9 +1,9 @@
1
1
  import { BaseCommand, ForkError } from "../baseCommand.js";
2
2
  import path from "node:path";
3
3
  import { debuglog } from "node:util";
4
- import { EggType, ImportResolveError, detectType, importResolve } from "@eggjs/utils";
5
4
  import { Args, Flags } from "@oclif/core";
6
5
  import fs from "node:fs/promises";
6
+ import { EggType, ImportResolveError, detectType, importResolve } from "@eggjs/utils";
7
7
  import ciParallelVars from "ci-parallel-vars";
8
8
  import globby from "globby";
9
9
  import { getChangedFilesForRoots } from "jest-changed-files";
@@ -45,6 +45,11 @@ var Test = class extends BaseCommand {
45
45
  description: "run tests in watch mode",
46
46
  default: false,
47
47
  char: "w"
48
+ }),
49
+ pool: Flags.string({
50
+ description: "vitest worker pool type",
51
+ options: ["forks", "threads"],
52
+ default: process.env.EGG_VITEST_POOL ?? "threads"
48
53
  })
49
54
  };
50
55
  async run() {
@@ -89,6 +94,8 @@ var Test = class extends BaseCommand {
89
94
  return (path.isAbsolute(f) ? f : path.join(flags.base, f)).replace(/\\/g, "/");
90
95
  });
91
96
  process.env.EGG_BIN_TIMEOUT = String(flags.timeout);
97
+ process.env.EGG_VITEST_POOL = flags.pool;
98
+ process.env.EGG_VITEST_ISOLATE = process.env.EGG_VITEST_ISOLATE ?? "false";
92
99
  debug("run test with vitest, files: %o, flags: %o", files, flags);
93
100
  const config = await this.buildVitestConfig(files);
94
101
  if (flags["dry-run"]) {
@@ -120,22 +127,24 @@ var Test = class extends BaseCommand {
120
127
  setupFiles.push(...requires);
121
128
  const eggType = await detectType(flags.base);
122
129
  debug("eggType: %s", eggType);
123
- if (eggType === EggType.application) try {
130
+ const isSelfTestFixture = !!process.env.EGG_BIN_SELF_TEST_FIXTURE;
131
+ if (!isSelfTestFixture && eggType === EggType.application) try {
124
132
  const mockSetup = importResolve("@eggjs/mock/setup_vitest", { paths: [flags.base] });
125
133
  setupFiles.push(mockSetup);
126
134
  debug("auto add @eggjs/mock/setup_vitest: %o", mockSetup);
127
135
  } catch (err) {
128
136
  if (!(err instanceof ImportResolveError)) throw err;
129
- debug("skip @eggjs/mock/setup_vitest: @eggjs/mock not installed");
137
+ debug("skip @eggjs/mock/setup_vitest: not resolvable");
130
138
  }
131
139
  let runner;
132
- try {
133
- runner = importResolve("@eggjs/tegg-vitest/runner", { paths: [flags.base] });
134
- debug("auto use @eggjs/tegg-vitest/runner: %o", runner);
140
+ if (!isSelfTestFixture) for (const resolveFrom of [flags.base, import.meta.dirname]) try {
141
+ runner = importResolve("@eggjs/tegg-vitest/runner", { paths: [resolveFrom] });
142
+ debug("auto use @eggjs/tegg-vitest/runner from %s: %o", resolveFrom, runner);
143
+ break;
135
144
  } catch (err) {
136
145
  if (!(err instanceof ImportResolveError)) throw err;
137
- debug("skip @eggjs/tegg-vitest/runner: @eggjs/tegg-vitest not installed");
138
146
  }
147
+ if (!runner) debug("skip @eggjs/tegg-vitest/runner: self-test fixture or not resolvable");
139
148
  return {
140
149
  root: flags.base,
141
150
  include: files,
@@ -145,12 +154,15 @@ var Test = class extends BaseCommand {
145
154
  "**/node_modules/**"
146
155
  ],
147
156
  testTimeout: flags.timeout,
157
+ hookTimeout: flags.timeout,
148
158
  testNamePattern: flags.grep,
149
159
  bail: flags.bail ? 1 : 0,
150
160
  setupFiles,
151
161
  runner,
152
162
  reporters: [process.env.TEST_REPORTER ?? "default"],
153
- pool: "forks",
163
+ pool: flags.pool,
164
+ isolate: process.env.EGG_VITEST_ISOLATE !== "false",
165
+ fileParallelism: process.env.EGG_FILE_PARALLELISM === "true",
154
166
  execArgv: [...this.globalExecArgv],
155
167
  watch: flags.watch,
156
168
  globals: true,
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { PackageEgg } from "./types.js";
2
2
  import { BaseCommand, ForkError, ForkNodeOptions } from "./baseCommand.js";
3
+ import Bundle from "./commands/bundle.js";
3
4
  import Test from "./commands/test.js";
4
5
  import Cov from "./commands/cov.js";
5
6
  import Dev from "./commands/dev.js";
7
+ import Manifest from "./commands/manifest.js";
6
8
  export * from "@oclif/core";
7
- export { BaseCommand, Cov, Dev, ForkError, ForkNodeOptions, PackageEgg, Test };
9
+ export { BaseCommand, Bundle, Cov, Dev, ForkError, ForkNodeOptions, Manifest, PackageEgg, Test };
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { BaseCommand, ForkError } from "./baseCommand.js";
2
+ import Bundle from "./commands/bundle.js";
2
3
  import Test from "./commands/test.js";
3
4
  import Cov from "./commands/cov.js";
4
5
  import Dev from "./commands/dev.js";
6
+ import Manifest from "./commands/manifest.js";
5
7
 
6
8
  export * from "@oclif/core"
7
9
 
8
- export { BaseCommand, Cov, Dev, ForkError, Test };
10
+ export { BaseCommand, Bundle, Cov, Dev, ForkError, Manifest, Test };
@@ -0,0 +1,59 @@
1
+ import { debuglog } from 'node:util';
2
+
3
+ import { importModule } from '@eggjs/utils';
4
+
5
+ const debug = debuglog('egg/bin/scripts/manifest-generate');
6
+
7
+ async function main() {
8
+ debug('argv: %o', process.argv);
9
+ const options = JSON.parse(process.argv[2]);
10
+ debug('manifest generate options: %o', options);
11
+
12
+ // Set server env/scope before importing framework
13
+ if (options.env) {
14
+ process.env.EGG_SERVER_ENV = options.env;
15
+ }
16
+ if (options.scope) {
17
+ process.env.EGG_SERVER_SCOPE = options.scope;
18
+ }
19
+
20
+ // Clean any existing manifest before generation to ensure the collector
21
+ // captures all lookups (not just cache misses from a stale manifest).
22
+ const { ManifestStore } = await importModule('@eggjs/core', {
23
+ paths: [options.framework],
24
+ });
25
+ ManifestStore.clean(options.baseDir);
26
+
27
+ const framework = await importModule(options.framework);
28
+ const app = await framework.start({
29
+ baseDir: options.baseDir,
30
+ framework: options.framework,
31
+ env: options.env,
32
+ metadataOnly: true,
33
+ });
34
+
35
+ // Generate manifest from collected metadata
36
+ const manifest = app.loader.generateManifest();
37
+
38
+ // Write manifest to .egg/manifest.json
39
+ await ManifestStore.write(options.baseDir, manifest);
40
+
41
+ // Log stats
42
+ const resolveCacheCount = Object.keys(manifest.resolveCache).length;
43
+ const fileDiscoveryCount = Object.keys(manifest.fileDiscovery).length;
44
+ const extensionCount = Object.keys(manifest.extensions).length;
45
+ console.log('[manifest] Generated manifest v%d at %s', manifest.version, manifest.generatedAt);
46
+ console.log('[manifest] resolveCache entries: %d', resolveCacheCount);
47
+ console.log('[manifest] fileDiscovery entries: %d', fileDiscoveryCount);
48
+ console.log('[manifest] extension entries: %d', extensionCount);
49
+ console.log('[manifest] Written to %s/.egg/manifest.json', options.baseDir);
50
+
51
+ // Clean up and exit
52
+ await app.close();
53
+ process.exit(0);
54
+ }
55
+
56
+ main().catch((err) => {
57
+ console.error('[manifest] Generation failed:', err);
58
+ process.exit(1);
59
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eggjs/bin",
3
- "version": "8.0.2-beta.1",
3
+ "version": "8.0.2-beta.10",
4
4
  "description": "egg developer tool",
5
5
  "homepage": "https://github.com/eggjs/egg/tree/next/tools/egg-bin",
6
6
  "bugs": {
@@ -28,8 +28,10 @@
28
28
  "exports": {
29
29
  ".": "./dist/index.js",
30
30
  "./baseCommand": "./dist/baseCommand.js",
31
+ "./commands/bundle": "./dist/commands/bundle.js",
31
32
  "./commands/cov": "./dist/commands/cov.js",
32
33
  "./commands/dev": "./dist/commands/dev.js",
34
+ "./commands/manifest": "./dist/commands/manifest.js",
33
35
  "./commands/test": "./dist/commands/test.js",
34
36
  "./types": "./dist/types.js",
35
37
  "./utils": "./dist/utils.js",
@@ -49,7 +51,10 @@
49
51
  "tsconfig-paths": "^4.2.0",
50
52
  "utility": "^2.5.0",
51
53
  "vitest": "^4.0.15",
52
- "@eggjs/utils": "5.0.2-beta.1"
54
+ "@eggjs/tegg-vitest": "4.0.2-beta.10",
55
+ "@eggjs/core": "7.0.2-beta.10",
56
+ "@eggjs/utils": "5.0.2-beta.10",
57
+ "@eggjs/egg-bundler": "0.0.1-beta.0"
53
58
  },
54
59
  "devDependencies": {
55
60
  "@swc-node/register": "^1.11.1",
@@ -63,14 +68,15 @@
63
68
  "npminstall": "^7.12.0",
64
69
  "rimraf": "^6.1.2",
65
70
  "runscript": "^2.0.1",
71
+ "sdk-base": "^5.0.1",
66
72
  "typescript": "^5.9.3",
67
- "@eggjs/mock": "7.0.2-beta.1",
68
- "@eggjs/tsconfig": "3.1.2-beta.1",
69
- "@eggjs/supertest": "9.0.2-beta.1",
70
- "egg": "4.1.2-beta.1"
73
+ "@eggjs/mock": "7.0.2-beta.10",
74
+ "@eggjs/supertest": "9.0.2-beta.10",
75
+ "egg": "4.1.2-beta.10",
76
+ "@eggjs/tsconfig": "3.1.2-beta.10"
71
77
  },
72
78
  "peerDependencies": {
73
- "@eggjs/mock": "7.0.2-beta.1"
79
+ "@eggjs/mock": "7.0.2-beta.10"
74
80
  },
75
81
  "peerDependenciesMeta": {
76
82
  "@eggjs/mock": {
@@ -90,8 +96,8 @@
90
96
  "node": ">=22.18.0"
91
97
  },
92
98
  "scripts": {
99
+ "build": "tsdown -c tsdown.config.ts",
93
100
  "typecheck": "tsgo --noEmit",
94
- "pretest": "tsdown",
95
101
  "test": "vitest run",
96
102
  "cov": "vitest run --coverage",
97
103
  "ci": "npm run cov"
@@ -0,0 +1,59 @@
1
+ import { debuglog } from 'node:util';
2
+
3
+ import { importModule } from '@eggjs/utils';
4
+
5
+ const debug = debuglog('egg/bin/scripts/manifest-generate');
6
+
7
+ async function main() {
8
+ debug('argv: %o', process.argv);
9
+ const options = JSON.parse(process.argv[2]);
10
+ debug('manifest generate options: %o', options);
11
+
12
+ // Set server env/scope before importing framework
13
+ if (options.env) {
14
+ process.env.EGG_SERVER_ENV = options.env;
15
+ }
16
+ if (options.scope) {
17
+ process.env.EGG_SERVER_SCOPE = options.scope;
18
+ }
19
+
20
+ // Clean any existing manifest before generation to ensure the collector
21
+ // captures all lookups (not just cache misses from a stale manifest).
22
+ const { ManifestStore } = await importModule('@eggjs/core', {
23
+ paths: [options.framework],
24
+ });
25
+ ManifestStore.clean(options.baseDir);
26
+
27
+ const framework = await importModule(options.framework);
28
+ const app = await framework.start({
29
+ baseDir: options.baseDir,
30
+ framework: options.framework,
31
+ env: options.env,
32
+ metadataOnly: true,
33
+ });
34
+
35
+ // Generate manifest from collected metadata
36
+ const manifest = app.loader.generateManifest();
37
+
38
+ // Write manifest to .egg/manifest.json
39
+ await ManifestStore.write(options.baseDir, manifest);
40
+
41
+ // Log stats
42
+ const resolveCacheCount = Object.keys(manifest.resolveCache).length;
43
+ const fileDiscoveryCount = Object.keys(manifest.fileDiscovery).length;
44
+ const extensionCount = Object.keys(manifest.extensions).length;
45
+ console.log('[manifest] Generated manifest v%d at %s', manifest.version, manifest.generatedAt);
46
+ console.log('[manifest] resolveCache entries: %d', resolveCacheCount);
47
+ console.log('[manifest] fileDiscovery entries: %d', fileDiscoveryCount);
48
+ console.log('[manifest] extension entries: %d', extensionCount);
49
+ console.log('[manifest] Written to %s/.egg/manifest.json', options.baseDir);
50
+
51
+ // Clean up and exit
52
+ await app.close();
53
+ process.exit(0);
54
+ }
55
+
56
+ main().catch((err) => {
57
+ console.error('[manifest] Generation failed:', err);
58
+ process.exit(1);
59
+ });