Skip to content

Commit

Permalink
refactor: use async fs + expose createServer API
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Apr 20, 2020
1 parent 33488fe commit bfb4b91
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 7,540 deletions.
5 changes: 4 additions & 1 deletion bin/vds.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
#!/usr/bin/env node
require('../lib/server')
const { createServer } = require('../lib/server')

// TODO pass cli args
createServer()
4 changes: 2 additions & 2 deletions lib/hmrWatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ exports.createFileWatcher = (notify) => {
ignored: [/node_modules/]
})

fileWatcher.on('change', (file) => {
fileWatcher.on('change', async (file) => {
const resourcePath = '/' + path.relative(process.cwd(), file)

if (file.endsWith('.vue')) {
// check which part of the file changed
const [descriptor, prevDescriptor] = parseSFC(file)
const [descriptor, prevDescriptor] = await parseSFC(file)
if (!prevDescriptor) {
// the file has never been accessed yet
return
Expand Down
25 changes: 13 additions & 12 deletions lib/moduleMiddleware.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
const fs = require('fs')
const path = require('path')
const { sendJS } = require('./utils')
const resolve = require('resolve-cwd')
const { sendJSStream } = require('./utils')

exports.moduleMiddleware = (id, res) => {
let modulePath
// try node resolve first
// TODO support custom imports map e.g. for snowpack web_modules

// fallback to node resolve
try {
modulePath = require.resolve(id)
modulePath = resolve(id)
if (id === 'vue') {
modulePath = path.join(
path.dirname(modulePath),
'dist/vue.runtime.esm-browser.js'
)
}
} catch (e) {
res.setStatus(404)
res.end()
}

// TODO resolve snowpack web_modules

if (id === 'vue') {
// modulePath = path.relative(modulePath, 'dist/vue.runtime.esm-browser.js')
modulePath = path.resolve(__dirname, 'vue.js')
}

sendJS(res, fs.readFileSync(modulePath))
sendJSStream(res, modulePath)
}
6 changes: 3 additions & 3 deletions lib/parseSFC.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const fs = require('fs')
const fs = require('fs').promises
const { parse } = require('@vue/compiler-sfc')

const cache = new Map()

exports.parseSFC = (filename, saveCache = false) => {
const content = fs.readFileSync(filename, 'utf-8')
exports.parseSFC = async (filename, saveCache = false) => {
const content = await fs.readFile(filename, 'utf-8')
const { descriptor, errors } = parse(content, {
filename
})
Expand Down
105 changes: 69 additions & 36 deletions lib/server.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const fs = require('fs')
const fs = require('fs').promises
const path = require('path')
const http = require('http')
const url = require('url')
Expand All @@ -10,44 +10,77 @@ const { createFileWatcher } = require('./hmrWatcher')
const { sendJS } = require('./utils')
const { rewrite } = require('./moduleRewriter')

const hmrClientCode = fs.readFileSync(path.resolve(__dirname, './hmrClient.js'))

const server = http.createServer((req, res) => {
const pathname = url.parse(req.url).pathname
if (pathname === '/__hmrClient') {
return sendJS(res, hmrClientCode)
} else if (pathname.startsWith('/__modules/')) {
return moduleMiddleware(pathname.replace('/__modules/', ''), res)
} else if (pathname.endsWith('.vue')) {
return vue(req, res)
} else if (pathname.endsWith('.js')) {
const filename = path.join(process.cwd(), pathname.slice(1))
if (fs.existsSync(filename)) {
const content = rewrite(fs.readFileSync(filename, 'utf-8'))
return sendJS(res, content)
exports.createServer = async ({ port = 3000 } = {}) => {
const hmrClientCode = await fs.readFile(
path.resolve(__dirname, './hmrClient.js')
)

const server = http.createServer(async (req, res) => {
const pathname = url.parse(req.url).pathname
if (pathname === '/__hmrClient') {
return sendJS(res, await hmrClientCode)
} else if (pathname.startsWith('/__modules/')) {
return moduleMiddleware(pathname.replace('/__modules/', ''), res)
} else if (pathname.endsWith('.vue')) {
return vue(req, res)
} else if (pathname.endsWith('.js')) {
const filename = path.join(process.cwd(), pathname.slice(1))
try {
const content = await fs.readFile(filename, 'utf-8')
return sendJS(res, rewrite(content))
} catch (e) {
if (e.code === 'ENOENT') {
// fallthrough to serve-handler
} else {
console.error(e)
}
}
}
}

serve(req, res, {
rewrites: [{ source: '**', destination: '/index.html' }]
serve(req, res, {
rewrites: [{ source: '**', destination: '/index.html' }]
})
})
})

const wss = new ws.Server({ server })
const sockets = new Set()
wss.on('connection', (socket) => {
sockets.add(socket)
socket.send(JSON.stringify({ type: 'connected' }))
socket.on('close', () => {
sockets.delete(socket)

const wss = new ws.Server({ server })
const sockets = new Set()

wss.on('connection', (socket) => {
sockets.add(socket)
socket.send(JSON.stringify({ type: 'connected' }))
socket.on('close', () => {
sockets.delete(socket)
})
})
})

createFileWatcher((payload) =>
sockets.forEach((s) => s.send(JSON.stringify(payload)))
)
wss.on('error', (e) => {
if (e.code !== 'EADDRINUSE') {
console.error(e)
}
})

// TODO customized port
server.listen(3000, () => {
console.log('Running at http://localhost:3000')
})
createFileWatcher((payload) =>
sockets.forEach((s) => s.send(JSON.stringify(payload)))
)

return new Promise((resolve, reject) => {
server.on('error', (e) => {
if (e.code === 'EADDRINUSE') {
console.log(`port ${port} is in use, trying another one...`)
setTimeout(() => {
server.close()
server.listen(++port)
}, 100)
} else {
console.error(e)
}
})

server.on('listening', () => {
console.log(`Running at http://localhost:${port}`)
resolve()
})

server.listen(port)
})
}
14 changes: 14 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const fs = require('fs')

function send(res, source, mime) {
res.setHeader('Content-Type', mime)
res.end(source)
Expand All @@ -7,5 +9,17 @@ function sendJS(res, source) {
send(res, source, 'application/javascript')
}

function sendJSStream(res, file) {
res.setHeader('Content-Type', 'application/javascript')
const stream = fs.createReadStream(file)
stream.on('open', () => {
stream.pipe(res)
})
stream.on('error', (err) => {
res.end(err)
})
}

exports.send = send
exports.sendJS = sendJS
exports.sendJSStream = sendJSStream
Loading

0 comments on commit bfb4b91

Please sign in to comment.