@applicvision/js-toolbox
Version:
A collection of tools for modern JavaScript development
100 lines (81 loc) • 2.74 kB
JavaScript
import style from '@applicvision/js-toolbox/style'
import fs from "node:fs"
import path from "node:path"
import http, { OutgoingMessage } from "node:http"
import https from "node:https"
const clientScriptPath = new URL('./client.js', import.meta.url)
/**
* @param {{port: number, certificate?: string, paths: string[], ignorePatterns: string[]}} options
*/
export function createServer(options) {
let running = false
function logState() {
if (!running) return
console.clear()
console.log('==== Autoreload server ====')
console.log()
console.log('Watching for changes in:', options.paths.map(path => style.bold(path)).join(', '))
if (options.ignorePatterns.length) {
console.log('Ignoring files including:', options.ignorePatterns.map(path => style.bold(path)).join(', '))
}
console.log()
if (attachedClients.size == 0) {
const url = `${options.certificate ? 'https' : 'http'}://localhost:${options.port}/autoreload.js`
console.log(`Welcome to auto reload. In your web app, include ${style.underline(url)} to get started.`)
} else {
console.log('Connected clients:', attachedClients.size)
}
}
/** @type {Set<OutgoingMessage>} */
const attachedClients = new Set()
const { certificate = process.env.JS_TOOLBOX_CERT } = options
const server = certificate ? https.createServer({
key: fs.readFileSync(path.resolve(certificate, 'key.pem')),
cert: fs.readFileSync(path.resolve(certificate, 'cert.pem'))
}) : http.createServer()
server.on('request', async (request, response) => {
const path = request.url
if (path == '/autoreload.js') {
response.writeHead(200, {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/javascript'
})
return fs.createReadStream(clientScriptPath).pipe(response)
}
if (path == '/stream') {
response.writeHead(200, {
'content-type': 'text/event-stream',
'Access-Control-Allow-Origin': '*',
})
attachedClients.add(response)
response.write(`event: welcome\ndata: ${Date.now()}\n\n`)
response.on('close', () => {
attachedClients.delete(response)
logState()
})
return logState()
}
response.end(`Welcome to auto reload. In your web app, import ${certificate ? 'https' : 'http'}://${request.headers.host}/autoreload.js to get started.`)
})
return {
start(port) {
running = true
return new Promise(resolve => server.listen(port, () => {
logState()
resolve()
}))
},
notifyClients(filename) {
attachedClients.forEach(stream => {
stream.write(`event: filechange\ndata: ${filename}\n\n`)
})
},
stop() {
running = false
attachedClients.forEach(stream => {
stream.end(`event: stop\ndata: ${Date.now()}\n\n`)
})
server.close()
}
}
}