lvyjs 0.3.0-alpha.0 → 0.3.0-alpha.1
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/bin/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { fork } from 'child_process'
|
|
4
4
|
import { join, dirname, relative } from 'path'
|
|
5
5
|
import { fileURLToPath } from 'node:url'
|
|
6
|
+
import chokidar from 'chokidar'
|
|
6
7
|
const args = [...process.argv.slice(2)]
|
|
7
8
|
const currentFilePath = fileURLToPath(import.meta.url)
|
|
8
9
|
const currentDirPath = dirname(currentFilePath)
|
|
@@ -23,6 +24,58 @@ const dev = () => {
|
|
|
23
24
|
|
|
24
25
|
let devProcess = null
|
|
25
26
|
let pendingRestart = false // 标记是否有重启 pending
|
|
27
|
+
let killTimeout = null // SIGKILL 超时定时器
|
|
28
|
+
let restartDebounceTimer = null // 重启防抖定时器
|
|
29
|
+
let latestConfig = null // 最新待应用的配置
|
|
30
|
+
let fileWatcher = null // chokidar watcher 实例
|
|
31
|
+
let watchDebounce = null // watch 防抖定时器
|
|
32
|
+
|
|
33
|
+
// 解析 watch 配置
|
|
34
|
+
const parseWatchConfig = data => {
|
|
35
|
+
const w = data?.watch
|
|
36
|
+
if (!w) return { paths: [], delay: 500 }
|
|
37
|
+
if (Array.isArray(w)) return { paths: w, delay: 500 }
|
|
38
|
+
if (typeof w === 'object' && w.paths) {
|
|
39
|
+
return { paths: w.paths, delay: w.delay || 500 }
|
|
40
|
+
}
|
|
41
|
+
return { paths: [], delay: 500 }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 关闭 watcher
|
|
45
|
+
const closeFileWatcher = async () => {
|
|
46
|
+
if (watchDebounce) {
|
|
47
|
+
clearTimeout(watchDebounce)
|
|
48
|
+
watchDebounce = null
|
|
49
|
+
}
|
|
50
|
+
if (fileWatcher) {
|
|
51
|
+
await fileWatcher.close()
|
|
52
|
+
fileWatcher = null
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 启动 watch 监听(支持 glob 模式,如 'src/**/*.{ts,tsx}')
|
|
57
|
+
const startFileWatcher = data => {
|
|
58
|
+
closeFileWatcher()
|
|
59
|
+
const { paths, delay } = parseWatchConfig(data)
|
|
60
|
+
if (paths.length === 0) return
|
|
61
|
+
|
|
62
|
+
fileWatcher = chokidar.watch(paths, {
|
|
63
|
+
cwd: process.cwd(),
|
|
64
|
+
ignoreInitial: true,
|
|
65
|
+
ignored: /(^|[\/\\])\../ // 忽略点文件
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
fileWatcher.on('all', (_event, filePath) => {
|
|
69
|
+
if (watchDebounce) clearTimeout(watchDebounce)
|
|
70
|
+
watchDebounce = setTimeout(() => {
|
|
71
|
+
watchDebounce = null
|
|
72
|
+
console.info(`[lvyjs] 监听到文件变化: ${filePath},正在重启...`)
|
|
73
|
+
doRestart(latestConfig || data)
|
|
74
|
+
}, delay)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
fileWatcher.on('ready', () => {})
|
|
78
|
+
}
|
|
26
79
|
|
|
27
80
|
// 启动开发进程
|
|
28
81
|
const startDevProcess = data => {
|
|
@@ -31,6 +84,12 @@ const dev = () => {
|
|
|
31
84
|
const envConfig = data?.env || {}
|
|
32
85
|
// 前面1个被占用,剩余参数
|
|
33
86
|
const restArgs = args.slice(1)
|
|
87
|
+
|
|
88
|
+
// 如果支持,得到 watch 配置,启动watch。
|
|
89
|
+
// 当watch的配置发生变化时,重启开发进程
|
|
90
|
+
// 复用当前的重启逻辑。
|
|
91
|
+
startFileWatcher(data)
|
|
92
|
+
|
|
34
93
|
devProcess = fork(indexFileDir, restArgs, {
|
|
35
94
|
stdio: 'inherit',
|
|
36
95
|
execArgv: [
|
|
@@ -67,11 +126,31 @@ const dev = () => {
|
|
|
67
126
|
})
|
|
68
127
|
}
|
|
69
128
|
|
|
70
|
-
//
|
|
129
|
+
// 优雅重启开发进程(带防抖,避免密集重启)
|
|
71
130
|
const restartDevProcess = newConfig => {
|
|
131
|
+
// 保存最新配置,防抖后使用
|
|
132
|
+
latestConfig = newConfig
|
|
133
|
+
|
|
134
|
+
if (restartDebounceTimer) clearTimeout(restartDebounceTimer)
|
|
135
|
+
restartDebounceTimer = setTimeout(() => {
|
|
136
|
+
restartDebounceTimer = null
|
|
137
|
+
doRestart(latestConfig)
|
|
138
|
+
}, 500)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const doRestart = config => {
|
|
142
|
+
// 关闭旧的文件监听
|
|
143
|
+
closeFileWatcher()
|
|
144
|
+
|
|
72
145
|
if (!devProcess) {
|
|
73
146
|
// 没有运行中的进程,直接启动
|
|
74
|
-
startDevProcess(
|
|
147
|
+
startDevProcess(config)
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 如果已经在等待重启,只更新配置,不重复 kill
|
|
152
|
+
if (pendingRestart) {
|
|
153
|
+
latestConfig = config
|
|
75
154
|
return
|
|
76
155
|
}
|
|
77
156
|
|
|
@@ -80,15 +159,22 @@ const dev = () => {
|
|
|
80
159
|
|
|
81
160
|
// 监听退出事件,确保旧进程完全结束后再启动新进程
|
|
82
161
|
devProcess.once('exit', () => {
|
|
162
|
+
// 清除 SIGKILL 超时定时器
|
|
163
|
+
if (killTimeout) {
|
|
164
|
+
clearTimeout(killTimeout)
|
|
165
|
+
killTimeout = null
|
|
166
|
+
}
|
|
83
167
|
pendingRestart = false
|
|
84
|
-
|
|
168
|
+
// 使用最新的配置启动
|
|
169
|
+
startDevProcess(latestConfig || config)
|
|
85
170
|
})
|
|
86
171
|
|
|
87
172
|
// 发送终止信号
|
|
88
173
|
devProcess.kill('SIGTERM')
|
|
89
174
|
|
|
90
175
|
// 超时保护:如果 5 秒后还没退出,强制杀掉
|
|
91
|
-
setTimeout(() => {
|
|
176
|
+
killTimeout = setTimeout(() => {
|
|
177
|
+
killTimeout = null
|
|
92
178
|
if (devProcess && !devProcess.killed) {
|
|
93
179
|
console.info('[lvyjs] 开发进程无响应,强制终止')
|
|
94
180
|
devProcess.kill('SIGKILL')
|
|
@@ -131,6 +217,7 @@ const dev = () => {
|
|
|
131
217
|
// 处理主进程退出
|
|
132
218
|
process.on('SIGINT', () => {
|
|
133
219
|
console.log('\n[lvyjs] 正在退出...')
|
|
220
|
+
closeFileWatcher()
|
|
134
221
|
if (devProcess) devProcess.kill()
|
|
135
222
|
if (readConfigFork) readConfigFork.kill()
|
|
136
223
|
process.exit(0)
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
.warning,
|
|
1
|
+
.warning,
|
|
2
|
+
.error,
|
|
3
|
+
.success,
|
|
4
|
+
.message {
|
|
2
5
|
border: 1px solid #ccc;
|
|
3
6
|
padding: 10px;
|
|
4
7
|
color: #333;
|
|
@@ -17,13 +20,13 @@
|
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
.info {
|
|
20
|
-
background: #
|
|
23
|
+
background: #a9a9a9;
|
|
21
24
|
box-shadow: 0 0 1px rgba(169, 169, 169, 0.25);
|
|
22
25
|
color: #fff;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
.alert {
|
|
26
|
-
background: #
|
|
29
|
+
background: #8b0000;
|
|
27
30
|
box-shadow: 0 0 1px rgba(139, 0, 0, 0.25);
|
|
28
31
|
color: #fff;
|
|
29
32
|
}
|
|
@@ -32,4 +35,4 @@
|
|
|
32
35
|
background: #006400;
|
|
33
36
|
box-shadow: 0 0 1px rgba(0, 100, 0, 0.25);
|
|
34
37
|
color: #fff;
|
|
35
|
-
}
|
|
38
|
+
}
|
|
@@ -24,7 +24,10 @@ body {
|
|
|
24
24
|
border-color: yellow;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
.warning,
|
|
27
|
+
.warning,
|
|
28
|
+
.error,
|
|
29
|
+
.success,
|
|
30
|
+
.message {
|
|
28
31
|
border: 1px solid #ccc;
|
|
29
32
|
padding: 10px;
|
|
30
33
|
color: #333;
|
|
@@ -52,4 +55,4 @@ body {
|
|
|
52
55
|
background: #8b0000;
|
|
53
56
|
box-shadow: 0 0 1px rgba(139, 0, 0, 0.25);
|
|
54
57
|
color: #fff;
|
|
55
|
-
}
|
|
58
|
+
}
|
package/lib/readConfig.js
CHANGED
|
@@ -56,11 +56,17 @@ const main = async () => {
|
|
|
56
56
|
};
|
|
57
57
|
// 首次发送
|
|
58
58
|
await sendConfig();
|
|
59
|
-
//
|
|
59
|
+
// 监听文件变化(带防抖,避免多次触发)
|
|
60
|
+
let debounceTimer = null;
|
|
60
61
|
watch(process.cwd(), (_event, filename) => {
|
|
61
62
|
if (files.includes(filename)) {
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
if (debounceTimer)
|
|
64
|
+
clearTimeout(debounceTimer);
|
|
65
|
+
debounceTimer = setTimeout(() => {
|
|
66
|
+
debounceTimer = null;
|
|
67
|
+
console.info(`[lvyjs] 配置文件 ${filename} 已变化,重新加载...`);
|
|
68
|
+
sendConfig();
|
|
69
|
+
}, 300);
|
|
64
70
|
}
|
|
65
71
|
});
|
|
66
72
|
// 保持进程运行
|