tldraw/scripts/workers/dev.ts
Kumi b3b61e941d
Some checks are pending
Checks / Tests & checks (push) Waiting to run
Checks / Build all projects (push) Waiting to run
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Waiting to run
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Waiting to run
End to end tests / End to end tests (push) Waiting to run
Publish Canary Packages / Publish Canary Packages (push) Waiting to run
Publish VS Code Extension / Publish VS Code Extension (push) Waiting to run
feat: migrate to Express.js for improved routing
Switched from `itty-router` to `Express.js` to enhance route handling and middleware support. Refactored the worker script to use `express` for routing and added middlewares for JSON and CORS handling. Updated development tooling to use `nodemon` for automatic restarts on file changes, improving the developer experience. Replaced `var` with `const` for better code practices and added PostgreSQL support with relevant schema.
2024-07-17 07:48:00 +02:00

156 lines
3.5 KiB
TypeScript

import { spawn } from 'child_process'
import kleur from 'kleur'
import { lock } from 'proper-lockfile'
import stripAnsi from 'strip-ansi'
const lockfileName = __dirname
class ProcessMonitor {
private process: ChildProcessWithoutNullStreams | null = null
constructor(
private command: string,
private args: string[] = []
) {}
public async start(): Promise<void> {
await this.stop() // Ensure any existing process is stopped
await this.lock()
await console.log(`Starting process...`)
this.process = spawn(this.command, this.args, {
env: {
NODE_ENV: 'development',
...process.env,
},
})
this.process.stdout.on('data', (data: Buffer) => {
this.handleOutput(stripAnsi(data.toString().replace('\r', '').trim()))
})
this.process.stderr.on('data', (data: Buffer) => {
this.handleOutput(stripAnsi(data.toString().replace('\r', '').trim()), true)
})
}
private handleOutput(output: string, err = false): void {
if (!output) return
if (output.includes('Ready on') && this.isLocked()) {
this.release()
}
if (output.includes('Segmentation fault')) {
console.error('Segmentation fault detected. Restarting process...')
this.restart()
} else if (!err) {
console.log(output) // or handle the output differently
} else {
console.error(output) // or handle the output differently
}
}
private async restart(): Promise<void> {
console.log('Restarting process...')
await this.stop()
setTimeout(() => this.start(), 3000) // Restart after a short delay
}
private async stop(): Promise<void> {
if (this.isLocked()) await this.release()
if (this.process) {
this.process.kill()
this.process = null
}
}
private _lockPromise?: Promise<() => Promise<void>>
private isLocked() {
return !!this._lockPromise
}
private async lock() {
if (this.isLocked()) throw new Error('Already locked')
console.log('Locking...')
this._lockPromise = lock(lockfileName, {
retries: {
minTimeout: 500,
retries: 10,
},
})
await this._lockPromise
console.log('Locked')
}
private async release() {
if (!this.isLocked()) throw new Error('Not locked')
console.log('Releasing...')
const lockPromise = this._lockPromise!
this._lockPromise = undefined
const release = await lockPromise
await release()
console.log('Released')
}
}
class SizeReporter {
lastLineTime = Date.now()
nextTick?: NodeJS.Timeout
size = 0
start() {
console.log('Spawning size reporter...')
const proc = spawn('yarn', [
'run',
'-T',
'esbuild',
'src/worker.ts',
'--bundle',
'--minify',
'--watch',
'--external:cloudflare:*',
'--target=esnext',
'--format=esm',
])
// listen for lines on stdin
proc.stdout.on('data', (data) => {
this.size += data.length
this.lastLineTime = Date.now()
clearTimeout(this.nextTick)
this.nextTick = setTimeout(() => {
console.log(
kleur.bold(kleur.yellow('worker')),
'is roughly',
kleur.bold(kleur.cyan(Math.floor(this.size / 1024) + 'kb')),
'(minified)\n'
)
this.size = 0
}, 10)
})
proc.stderr.on('data', (data) => {
console.log(data.toString())
})
process.on('SIGINT', () => {
console.log('Int')
proc.kill()
})
process.on('SIGTERM', () => {
console.log('Term')
proc.kill()
})
process.on('exit', () => {
console.log('Exiting')
proc.kill()
})
}
}
new ProcessMonitor('nodemon', [
'--watch',
'src',
'--exec',
'tsx',
'./src/worker.ts',
...process.argv.slice(2),
]).start()
new SizeReporter().start()