Skip to content

Commit b0bda37

Browse files
committed
refactor: enhance debug build process and improve HTTP server signal handling
1 parent 1dd66f2 commit b0bda37

2 files changed

Lines changed: 87 additions & 16 deletions

File tree

builder/source/debug.ts

Lines changed: 84 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
import * as Chokidar from 'chokidar'
32
import * as Process from 'node:process'
43
import * as Crypto from 'node:crypto'
@@ -9,34 +8,104 @@ import { SafeInitCwd } from './utils/safe-init-cwd.js'
98
import { Build } from './build-core.js'
109

1110
let ProjectRoot = SafeInitCwd({ Cwd: Process.cwd(), InitCwd: Process.env.INIT_CWD })
12-
const WatchingGlob: string[] = [];
13-
['builder/', 'userscript/', ''].forEach(Dir => {
11+
const WatchingGlob: string[] = []
12+
for (const Dir of ['builder/', 'userscript/', '']) {
1413
WatchingGlob.push(...Fs.globSync(`${ProjectRoot}/${Dir}source/**/*.ts`))
1514
WatchingGlob.push(...Fs.globSync(`${ProjectRoot}/${Dir}source/**/*.json`))
1615
WatchingGlob.push(...Fs.globSync(`${ProjectRoot}/${Dir}source/**/*.txt`))
17-
})
16+
}
1817
const Watcher = Chokidar.watch([...WatchingGlob], {
1918
ignored: '**/node_modules/**',
19+
ignoreInitial: true,
2020
})
2121

2222
let BuildCooldownTimer: NodeJS.Timeout | null = null
23-
let ShouldPreventHTTPResponse = false
23+
let ActiveBuildController: AbortController | null = null
24+
let BuildQueue: Promise<void> = Promise.resolve()
2425
let Version: number = 0
2526
let RandomPort = Crypto.randomInt(8000, 8999)
2627
const CoreBuildInstance = new Build({ Version: `0.0.${Version}`, Minify: false, BuildType: 'development', SubscriptionUrl: `http://localhost:${RandomPort}/tinyShield.dev.user.js` })
27-
await CoreBuildInstance.Init()
28-
Watcher.on('all', async (WatcherEvent, WatcherPath) => {
28+
29+
type DebugBuildContext = {
30+
Reason: 'initial' | 'change'
31+
DebounceMs: number
32+
WatcherEvent?: string
33+
WatcherPath?: string
34+
}
35+
36+
function EnqueueDebugBuild(BuildController: AbortController, Context: DebugBuildContext) {
37+
BuildQueue = BuildQueue.then(async () => {
38+
try {
39+
await RunDebugBuild(BuildController, Context)
40+
} catch (BuildError) {
41+
if (BuildController.signal.aborted) {
42+
return
43+
}
44+
45+
console.error('Debug build failed:', BuildError)
46+
if (ActiveBuildController === BuildController) {
47+
ActiveBuildController = null
48+
}
49+
}
50+
})
51+
}
52+
53+
function ScheduleDebugBuild(Context: DebugBuildContext) {
2954
if (BuildCooldownTimer) {
3055
clearTimeout(BuildCooldownTimer)
56+
BuildCooldownTimer = null
57+
}
58+
59+
ActiveBuildController?.abort()
60+
const BuildController = new AbortController()
61+
ActiveBuildController = BuildController
62+
63+
if (Context.DebounceMs <= 0) {
64+
EnqueueDebugBuild(BuildController, Context)
65+
return
66+
}
67+
68+
BuildCooldownTimer = setTimeout(() => {
69+
BuildCooldownTimer = null
70+
EnqueueDebugBuild(BuildController, Context)
71+
}, Context.DebounceMs)
72+
}
73+
74+
async function RunDebugBuild(BuildController: AbortController, Context: DebugBuildContext) {
75+
if (BuildController.signal.aborted) {
76+
return
3177
}
32-
BuildCooldownTimer = setTimeout(async () => {
78+
79+
if (Context.Reason === 'initial') {
80+
console.log('Starting initial debug build.')
81+
} else {
82+
const WatcherEvent = Context.WatcherEvent ?? 'unknown'
83+
const WatcherPath = Context.WatcherPath ?? 'unknown'
3384
console.log(`Detected file change (${WatcherEvent}):`, WatcherPath)
34-
ShouldPreventHTTPResponse = true
35-
await new StandardBuild(CoreBuildInstance, { Version: `0.0.${Version}`, Minify: false, BuildType: 'development', SubscriptionUrl: `http://localhost:${RandomPort}/tinyShield.dev.user.js` }).Build()
36-
Version++
37-
ShouldPreventHTTPResponse = false
38-
}, 1500)
85+
}
86+
87+
await new StandardBuild(CoreBuildInstance, { Version: `0.0.${Version}`, Minify: false, BuildType: 'development', SubscriptionUrl: `http://localhost:${RandomPort}/tinyShield.dev.user.js` }).Build()
88+
89+
if (BuildController.signal.aborted) {
90+
return
91+
}
92+
93+
Version++
94+
if (ActiveBuildController === BuildController) {
95+
ActiveBuildController = null
96+
}
97+
}
98+
99+
await CoreBuildInstance.Init()
100+
Watcher.on('all', (WatcherEvent, WatcherPath) => {
101+
ScheduleDebugBuild({
102+
Reason: 'change',
103+
DebounceMs: 1500,
104+
WatcherEvent,
105+
WatcherPath
106+
})
39107
})
40108

41-
RunDebugServer(RandomPort, ['tinyShield.dev.user.js'], ShouldPreventHTTPResponse)
42-
console.log(`Debug HTTP server running on http://localhost:${RandomPort}/tinyShield.dev.user.js`)
109+
RunDebugServer(RandomPort, ['tinyShield.dev.user.js'], () => ActiveBuildController?.signal ?? null)
110+
console.log(`Debug HTTP server running on http://localhost:${RandomPort}/tinyShield.dev.user.js`)
111+
ScheduleDebugBuild({ Reason: 'initial', DebounceMs: 0 })

builder/source/utils/http-server.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ function IsLoopBack(IP?: string) {
88
return IP === '127.0.0.1' || IP === '::1' || IP === '::ffff:127.0.0.1'
99
}
1010

11-
export function RunDebugServer(Port: number, FileName: string[], ShouldPreventHTTPResponse: boolean) {
11+
export function RunDebugServer(Port: number, FileName: string[], GetActiveBuildSignal: () => AbortSignal | null) {
1212
const HTTPServer = HTTP.createServer((Req, Res) => {
1313
let ProjectRoot = SafeInitCwd({ Cwd: Process.cwd(), InitCwd: Process.env.INIT_CWD })
1414
const RequestPath = Req.url?.substring(1) || ''
1515
const ResolvedPath = Path.resolve(ProjectRoot + '/dist', RequestPath)
1616
const RelativePath = Path.relative(ProjectRoot + '/dist', ResolvedPath)
17+
const ActiveBuildSignal = GetActiveBuildSignal()
18+
const ShouldPreventHTTPResponse = ActiveBuildSignal !== null && !ActiveBuildSignal.aborted
1719

1820
// Ensure the resolved path stays within the dist root to prevent directory traversal
1921
if (RelativePath.startsWith('..') || Path.isAbsolute(RelativePath)) {

0 commit comments

Comments
 (0)