@hile/cli 1.0.20 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ export * from './start.js';
package/dist/index.js CHANGED
@@ -1,66 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import pkg from '../package.json' with { type: 'json' };
3
3
  import { program } from 'commander';
4
- import { registerExitHook } from './exitHook.js';
5
- import { glob } from 'glob';
6
- import { resolve } from 'node:path';
7
- import { container, isService, loadService } from '@hile/core';
8
- import { createRequire } from 'node:module';
9
- const require = createRequire(import.meta.url);
10
- /** 加载 env 文件到 process.env(Node 20.12+ 原生 process.loadEnvFile) */
11
- function loadEnvFile(filePath) {
12
- process.loadEnvFile(resolve(process.cwd(), filePath));
13
- }
14
- const TAG = '[hile]';
15
- const c = {
16
- reset: '\x1b[0m',
17
- dim: '\x1b[2m',
18
- red: '\x1b[31m',
19
- green: '\x1b[32m',
20
- yellow: '\x1b[33m',
21
- cyan: '\x1b[36m',
22
- };
23
- const colorize = process.stdout.isTTY;
24
- function logContainerEvent(event) {
25
- const tag = colorize ? `${c.dim}${c.cyan}${TAG}${c.reset}` : TAG;
26
- const target = (s) => (colorize ? `${c.cyan}${s}${c.reset}` : s);
27
- const ok = (s) => (colorize ? `${c.green}${s}${c.reset}` : s);
28
- const warn = (s) => (colorize ? `${c.yellow}${s}${c.reset}` : s);
29
- const err = (s) => (colorize ? `${c.red}${s}${c.reset}` : s);
30
- const dim = (s) => (colorize ? `${c.dim}${s}${c.reset}` : s);
31
- switch (event.type) {
32
- case 'service:init':
33
- console.info(`${tag} ${target(`service#${event.id}`)} ${dim('init')}`);
34
- break;
35
- case 'service:ready':
36
- console.info(`${tag} ${target(`service#${event.id}`)} ${ok('ready')} ${dim(`(${event.durationMs}ms)`)}`);
37
- break;
38
- case 'service:error':
39
- console.error(`${tag} ${target(`service#${event.id}`)} ${err('failed')} ${dim(`(${event.durationMs}ms)`)}`);
40
- console.error(event.error);
41
- break;
42
- case 'service:shutdown:start':
43
- console.info(`${tag} ${target(`service#${event.id}`)} ${warn('stopping')}`);
44
- break;
45
- case 'service:shutdown:done':
46
- console.info(`${tag} ${target(`service#${event.id}`)} ${dim('stopped')} ${dim(`(${event.durationMs}ms)`)}`);
47
- break;
48
- case 'service:shutdown:error':
49
- console.error(`${tag} ${target(`service#${event.id}`)} ${err('shutdown error')}`);
50
- console.error(event.error);
51
- break;
52
- case 'container:shutdown:start':
53
- console.info(`${tag} ${target('container')} ${dim('shutdown start')}`);
54
- break;
55
- case 'container:shutdown:done':
56
- console.info(`${tag} ${target('container')} ${ok('shutdown done')} ${dim(`(${event.durationMs}ms)`)}`);
57
- break;
58
- case 'container:error':
59
- console.error(`${tag} ${target('container')} ${err('error')}`);
60
- console.error(event.error);
61
- break;
62
- }
63
- }
4
+ import { start } from './start.js';
64
5
  program.version(pkg.version, '-v, --version', '当前版本号');
65
6
  /**
66
7
  * 启动服务
@@ -77,54 +18,6 @@ program
77
18
  .option('-d, --dev', '开发模式', false)
78
19
  .option('-e, --env-file <path>', '加载指定 env 文件(兼容 Node --env-file 语义;可多次指定,先加载的不被后加载覆盖)', (v, acc) => (acc.push(v), acc), [])
79
20
  .description('启动服务,加载所有后缀为 boot.ts 或 boot.js 的服务,并注册退出钩子,在进程退出时销毁所有服务')
80
- .action(async (options) => {
81
- // 先加载 --env-file(与 Node --env-file 行为一致:先加载的优先,已存在的 key 不被覆盖)
82
- const envFiles = options.envFile ?? [];
83
- for (const p of envFiles) {
84
- loadEnvFile(p);
85
- }
86
- // 开发模式下,使用 tsx 运行
87
- if (options.dev) {
88
- await import('tsx/esm');
89
- process.env.NODE_ENV = 'development';
90
- }
91
- else {
92
- process.env.NODE_ENV = 'production';
93
- }
94
- const offEvent = process.env.NODE_ENV === 'development'
95
- ? container.on(logContainerEvent)
96
- : () => { };
97
- const cwd = process.cwd();
98
- const files = [];
99
- // 加载 package.json 文件
100
- // 如果 package.json 中存在 hile.auto_load_packages 属性,则加载该属性值中的所有服务
101
- // 该属性值中的每个元素必须是模块名称,不能是文件路径
102
- const packageJson = require(resolve(cwd, 'package.json'));
103
- if (packageJson.hile?.auto_load_packages && Array.isArray(packageJson.hile.auto_load_packages)) {
104
- for (let i = 0; i < packageJson.hile.auto_load_packages.length; i++) {
105
- files.push(packageJson.hile.auto_load_packages[i]);
106
- }
107
- }
108
- // 加载所有后缀为 boot.ts 或 boot.js 的服务
109
- const directory = resolve(cwd, process.env.HILE_RUNTIME_DIR || (options.dev ? 'src' : 'dist'));
110
- const _files = await glob(`**/*.boot.{ts,js}`, { cwd: directory });
111
- files.push(..._files.map(file => resolve(directory, file)));
112
- // 加载所有自启动服务
113
- // file: 文件路径或者模块名称
114
- await Promise.all(files.map(async (file) => {
115
- const target = await import(file);
116
- const fn = target?.default ?? target;
117
- if (!fn || !isService(fn))
118
- throw new Error(`invalid service file: ${file}`);
119
- await loadService(fn);
120
- }));
121
- // 如果没有服务要加载,则提示
122
- if (!files.length) {
123
- console.warn('no services to load');
124
- offEvent();
125
- return;
126
- }
127
- // 注册退出钩子,在进程退出时销毁所有服务
128
- registerExitHook(container, offEvent);
129
- });
21
+ .action((options) => start({ dev: options.dev, envFile: options.envFile }));
130
22
  program.parseAsync(process.argv);
23
+ export * from './start.js';
@@ -0,0 +1,4 @@
1
+ export declare function start(options: {
2
+ dev: boolean;
3
+ envFile?: string[];
4
+ }): Promise<void>;
package/dist/start.js ADDED
@@ -0,0 +1,115 @@
1
+ import { registerExitHook } from './exitHook.js';
2
+ import { glob } from 'glob';
3
+ import { resolve } from 'node:path';
4
+ import { container, isService, loadService } from '@hile/core';
5
+ import { createRequire } from 'node:module';
6
+ import { pathToFileURL } from 'node:url';
7
+ const require = createRequire(import.meta.url);
8
+ /** 加载 env 文件到 process.env(Node 20.12+ 原生 process.loadEnvFile) */
9
+ function loadEnvFile(filePath) {
10
+ process.loadEnvFile(resolve(process.cwd(), filePath));
11
+ }
12
+ const TAG = '[hile]';
13
+ const AUTO_TAG = '[auto]:';
14
+ const c = {
15
+ reset: '\x1b[0m',
16
+ dim: '\x1b[2m',
17
+ red: '\x1b[31m',
18
+ green: '\x1b[32m',
19
+ yellow: '\x1b[33m',
20
+ cyan: '\x1b[36m',
21
+ };
22
+ const colorize = process.stdout.isTTY;
23
+ function logContainerEvent(event) {
24
+ const tag = colorize ? `${c.dim}${c.cyan}${TAG}${c.reset}` : TAG;
25
+ const target = (s) => (colorize ? `${c.cyan}${s}${c.reset}` : s);
26
+ const ok = (s) => (colorize ? `${c.green}${s}${c.reset}` : s);
27
+ const warn = (s) => (colorize ? `${c.yellow}${s}${c.reset}` : s);
28
+ const err = (s) => (colorize ? `${c.red}${s}${c.reset}` : s);
29
+ const dim = (s) => (colorize ? `${c.dim}${s}${c.reset}` : s);
30
+ switch (event.type) {
31
+ case 'service:init':
32
+ console.info(`${tag} ${target(`service#${event.id}`)} ${dim('init')}`);
33
+ break;
34
+ case 'service:ready':
35
+ console.info(`${tag} ${target(`service#${event.id}`)} ${ok('ready')} ${dim(`(${event.durationMs}ms)`)}`);
36
+ break;
37
+ case 'service:error':
38
+ console.error(`${tag} ${target(`service#${event.id}`)} ${err('failed')} ${dim(`(${event.durationMs}ms)`)}`);
39
+ console.error(event.error);
40
+ break;
41
+ case 'service:shutdown:start':
42
+ console.info(`${tag} ${target(`service#${event.id}`)} ${warn('stopping')}`);
43
+ break;
44
+ case 'service:shutdown:done':
45
+ console.info(`${tag} ${target(`service#${event.id}`)} ${dim('stopped')} ${dim(`(${event.durationMs}ms)`)}`);
46
+ break;
47
+ case 'service:shutdown:error':
48
+ console.error(`${tag} ${target(`service#${event.id}`)} ${err('shutdown error')}`);
49
+ console.error(event.error);
50
+ break;
51
+ case 'container:shutdown:start':
52
+ console.info(`${tag} ${target('container')} ${dim('shutdown start')}`);
53
+ break;
54
+ case 'container:shutdown:done':
55
+ console.info(`${tag} ${target('container')} ${ok('shutdown done')} ${dim(`(${event.durationMs}ms)`)}`);
56
+ break;
57
+ case 'container:error':
58
+ console.error(`${tag} ${target('container')} ${err('error')}`);
59
+ console.error(event.error);
60
+ break;
61
+ }
62
+ }
63
+ export async function start(options) {
64
+ // 先加载 --env-file(与 Node --env-file 行为一致:先加载的优先,已存在的 key 不被覆盖)
65
+ const envFiles = options.envFile ?? [];
66
+ for (const p of envFiles) {
67
+ loadEnvFile(p);
68
+ }
69
+ // 开发模式下,使用 tsx 运行
70
+ if (options.dev) {
71
+ await import('tsx/esm');
72
+ process.env.NODE_ENV = 'development';
73
+ }
74
+ else {
75
+ process.env.NODE_ENV = 'production';
76
+ }
77
+ const offEvent = process.env.NODE_ENV === 'development'
78
+ ? container.on(logContainerEvent)
79
+ : () => { };
80
+ const cwd = process.cwd();
81
+ const files = [];
82
+ // 加载 package.json 文件
83
+ // 如果 package.json 中存在 hile.auto_load_packages 属性,则加载该属性值中的所有服务
84
+ // 该属性值中的每个元素必须是模块名称,不能是文件路径
85
+ const packageJson = require(resolve(cwd, 'package.json'));
86
+ if (packageJson.hile?.auto_load_packages && Array.isArray(packageJson.hile.auto_load_packages)) {
87
+ for (let i = 0; i < packageJson.hile.auto_load_packages.length; i++) {
88
+ files.push(AUTO_TAG + packageJson.hile.auto_load_packages[i]);
89
+ }
90
+ }
91
+ // 加载所有后缀为 boot.ts 或 boot.js 的服务
92
+ const directory = resolve(cwd, process.env.HILE_RUNTIME_DIR || (options.dev ? 'src' : 'dist'));
93
+ const _files = await glob(`**/*.boot.{ts,js}`, { cwd: directory });
94
+ files.push(..._files.map(file => resolve(directory, file)));
95
+ // 加载所有自启动服务
96
+ // file: 文件路径或者模块名称
97
+ await Promise.all(files.map(async (file) => {
98
+ const _file = file.startsWith(AUTO_TAG)
99
+ ? file.substring(AUTO_TAG.length)
100
+ : pathToFileURL(file).href;
101
+ const target = await import(_file);
102
+ const fn = target?.default ?? target;
103
+ if (!fn || !isService(fn))
104
+ throw new Error(`invalid service file: ${file}`);
105
+ await loadService(fn);
106
+ }));
107
+ // 如果没有服务要加载,则提示
108
+ if (!files.length) {
109
+ console.warn('no services to load');
110
+ offEvent();
111
+ return;
112
+ }
113
+ // 注册退出钩子,在进程退出时销毁所有服务
114
+ registerExitHook(container, offEvent);
115
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hile/cli",
3
- "version": "1.0.20",
3
+ "version": "1.0.22",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -25,11 +25,11 @@
25
25
  "vitest": "^4.0.18"
26
26
  },
27
27
  "dependencies": {
28
- "@hile/core": "^1.0.21",
28
+ "@hile/core": "^1.0.22",
29
29
  "async-exit-hook": "^2.0.1",
30
30
  "commander": "^14.0.3",
31
31
  "glob": "^13.0.6",
32
32
  "tsx": "^4.21.0"
33
33
  },
34
- "gitHead": "a913724465de986a7891746ced001ff6dcd3ff88"
34
+ "gitHead": "4ad0c56518461cbcc48c1dfe2fcb667303c5c889"
35
35
  }