Skip to content

Commit f706a49

Browse files
committed
Add Websockets based pinger.
1 parent d11c5b3 commit f706a49

File tree

5 files changed

+225
-60
lines changed

5 files changed

+225
-60
lines changed

client/on-demand-entries-client.js

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,64 @@
1-
/* global fetch, location */
1+
/* global fetch, location, WebSocket */
22

33
import Router from '../lib/router'
44
import 'whatwg-fetch'
55

6-
// Ping on every page change
7-
const originalOnRouteChangeComplete = Router.onRouteChangeComplete
8-
Router.onRouteChangeComplete = function (...args) {
9-
ping()
10-
if (originalOnRouteChangeComplete) originalOnRouteChangeComplete(...args)
6+
initiatePinging()
7+
.catch((err) => {
8+
console.error(err.stack)
9+
})
10+
11+
async function initiatePinging () {
12+
const res = await fetch('/on-demand-entries-pinger-port')
13+
const payload = await res.json()
14+
startPinging(payload.port)
1115
}
1216

13-
async function ping () {
14-
try {
15-
const url = `/on-demand-entries-ping?page=${Router.pathname}`
16-
const res = await fetch(url)
17-
const payload = await res.json()
18-
if (payload.invalid) {
19-
location.reload()
17+
async function retry () {
18+
while (true) {
19+
try {
20+
await initiatePinging()
21+
break
22+
} catch (err) {
23+
await new Promise((resolve) => setTimeout(resolve, 2000))
2024
}
21-
} catch (err) {
22-
console.error(`Error with on-demand-entries-ping: ${err.message}`)
2325
}
2426
}
2527

26-
async function runPinger () {
27-
while (true) {
28-
await new Promise((resolve) => setTimeout(resolve, 5000))
29-
await ping()
28+
function startPinging (port) {
29+
const conn = new WebSocket(`ws://localhost:${port}/`, 'protocolOne')
30+
let pingingHandler = null
31+
conn.onopen = () => {
32+
console.log('> Start pinging for the active page.')
33+
ping()
34+
pingingHandler = setInterval(ping, 1000 * 5)
3035
}
31-
}
3236

33-
runPinger()
34-
.catch((err) => {
35-
console.error(err)
36-
})
37+
conn.onmessage = (event) => {
38+
const parsedMessage = JSON.parse(event.data)
39+
if (parsedMessage.invalid) {
40+
location.reload()
41+
}
42+
}
43+
44+
conn.onclose = () => {
45+
clearInterval(pingingHandler)
46+
retry()
47+
}
48+
49+
conn.onerror = (error) => {
50+
console.log(`Pinger error: ${error.message}`)
51+
}
52+
53+
// Ping on every page change
54+
const originalOnRouteChangeComplete = Router.onRouteChangeComplete
55+
Router.onRouteChangeComplete = function (...args) {
56+
ping()
57+
if (originalOnRouteChangeComplete) originalOnRouteChangeComplete(...args)
58+
}
59+
60+
function ping () {
61+
const payload = { page: Router.pathname }
62+
conn.send(JSON.stringify(payload))
63+
}
64+
}

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"babel-preset-latest": "6.22.0",
5858
"babel-preset-react": "6.23.0",
5959
"babel-runtime": "6.23.0",
60+
"bufferutil": "2.0.1",
6061
"case-sensitive-paths-webpack-plugin": "1.1.4",
6162
"cross-spawn": "5.0.1",
6263
"del": "2.2.2",
@@ -79,14 +80,16 @@
7980
"source-map-support": "0.4.11",
8081
"strip-ansi": "3.0.1",
8182
"styled-jsx": "0.5.7",
82-
"touch": "^1.0.0",
83+
"touch": "1.0.0",
8384
"url": "0.11.0",
85+
"utf-8-validate": "3.0.1",
8486
"uuid": "3.0.1",
8587
"webpack": "2.2.1",
8688
"webpack-dev-middleware": "1.10.1",
8789
"webpack-hot-middleware": "2.17.0",
8890
"whatwg-fetch": "2.0.2",
89-
"write-file-webpack-plugin": "3.4.2"
91+
"write-file-webpack-plugin": "3.4.2",
92+
"ws": "2.1.0"
9093
},
9194
"devDependencies": {
9295
"babel-eslint": "7.1.1",

server/hot-reloader.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,13 @@ export default class HotReloader {
4949

5050
async stop () {
5151
if (this.webpackDevMiddleware) {
52-
return new Promise((resolve, reject) => {
52+
await new Promise((resolve, reject) => {
5353
this.webpackDevMiddleware.close((err) => {
5454
if (err) return reject(err)
5555
resolve()
5656
})
5757
})
58+
await this.onDemandEntries.close()
5859
}
5960
}
6061

@@ -149,7 +150,7 @@ export default class HotReloader {
149150
})
150151

151152
this.webpackHotMiddleware = webpackHotMiddleware(compiler, { log: false })
152-
this.onDemandEntries = onDemandEntryHandler(this.webpackDevMiddleware, compiler, {
153+
this.onDemandEntries = await onDemandEntryHandler(this.webpackDevMiddleware, compiler, {
153154
dir: this.dir,
154155
dev: true,
155156
...this.config.onDemandEntries

server/on-demand-entry-handler.js

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import DynamicEntryPlugin from 'webpack/lib/DynamicEntryPlugin'
22
import { EventEmitter } from 'events'
3+
import { createServer } from 'http'
34
import { join } from 'path'
4-
import { parse } from 'url'
55
import resolvePath from './resolve'
66
import touch from 'touch'
7+
import WebSocket from 'ws'
78

89
const ADDED = Symbol()
910
const BUILDING = Symbol()
1011
const BUILT = Symbol()
1112

12-
export default function onDemandEntryHandler (devMiddleware, compiler, {
13+
export default async function onDemandEntryHandler (devMiddleware, compiler, {
1314
dir,
1415
dev,
1516
maxInactiveAge = 1000 * 25
1617
}) {
18+
const server = await getServer()
19+
const wss = new WebSocket.Server({ server })
20+
1721
const entries = {}
1822
const lastAccessPages = ['']
1923
const doneCallbacks = new EventEmitter()
@@ -58,6 +62,31 @@ export default function onDemandEntryHandler (devMiddleware, compiler, {
5862
disposeInactiveEntries(devMiddleware, entries, lastAccessPages, maxInactiveAge)
5963
}, 5000)
6064

65+
wss.on('connection', (conn) => {
66+
conn.on('message', (message) => {
67+
const parsedMessage = JSON.parse(message)
68+
const page = normalizePage(parsedMessage.page)
69+
const entryInfo = entries[page]
70+
71+
// If there's no entry.
72+
// Then it seems like an weird issue.
73+
if (!entryInfo) {
74+
const message = `Client pings, but there's no entry for page: ${page}`
75+
console.error(message)
76+
conn.send(JSON.stringify({ invalid: true }))
77+
return
78+
}
79+
80+
// We don't need to maintain active state of anything other than BUILT entries
81+
if (entryInfo.status !== BUILT) return
82+
83+
// If there's an entryInfo
84+
lastAccessPages.pop()
85+
lastAccessPages.unshift(page)
86+
entryInfo.lastActiveTime = Date.now()
87+
})
88+
})
89+
6190
return {
6291
async ensurePage (page) {
6392
page = normalizePage(page)
@@ -103,31 +132,25 @@ export default function onDemandEntryHandler (devMiddleware, compiler, {
103132

104133
middleware () {
105134
return function (req, res, next) {
106-
if (!/^\/on-demand-entries-ping/.test(req.url)) return next()
107-
108-
const { query } = parse(req.url, true)
109-
const page = normalizePage(query.page)
110-
const entryInfo = entries[page]
135+
if (!/^\/on-demand-entries-pinger-port/.test(req.url)) return next()
111136

112-
// If there's no entry.
113-
// Then it seems like an weird issue.
114-
if (!entryInfo) {
115-
const message = `Client pings, but there's no entry for page: ${page}`
116-
console.error(message)
117-
sendJson(res, { invalid: true })
118-
return
119-
}
120-
121-
// We don't need to maintain active state of anything other than BUILT entries
122-
if (entryInfo.status !== BUILT) return
137+
sendJson(res, {
138+
port: server.address().port
139+
})
140+
}
141+
},
123142

124-
// If there's an entryInfo
125-
lastAccessPages.pop()
126-
lastAccessPages.unshift(page)
127-
entryInfo.lastActiveTime = Date.now()
143+
close () {
144+
return new Promise((resolve, reject) => {
145+
server.close((err) => {
146+
if (err) {
147+
reject(err)
148+
return
149+
}
128150

129-
sendJson(res, { success: true })
130-
}
151+
resolve()
152+
})
153+
})
131154
}
132155
}
133156
}
@@ -182,3 +205,17 @@ function sendJson (res, payload) {
182205
res.status = 200
183206
res.end(JSON.stringify(payload))
184207
}
208+
209+
function getServer () {
210+
return new Promise((resolve, reject) => {
211+
const server = createServer()
212+
server.listen((err) => {
213+
if (err) {
214+
reject(err)
215+
return
216+
}
217+
218+
resolve(server)
219+
})
220+
})
221+
}

0 commit comments

Comments
 (0)