Skip to content

Commit fb83a86

Browse files
Merge pull request cerebral#286 from cerebral/vscode
Vscode
2 parents e2d19c9 + 3313603 commit fb83a86

File tree

28 files changed

+699
-546
lines changed

28 files changed

+699
-546
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"lint": "eslint \"**/*.js\" \"**/*.ts\"",
1616
"precommit": "lint-staged",
1717
"release": "repo-cooker --release",
18-
"release:vscode": "cd packages/node_modules/overmind-devtools-vscode && cp -R ../overmind-devtools-client/dist devtoolsDist && vsce publish -p $VSCODE",
18+
"release:vscode": "cd packages/node_modules/overmind-devtools-vscode && vsce publish -p $VSCODE",
1919
"test": "repo-cooker test --no-parallel"
2020
},
2121
"heroku-run-build-script": false,

packages/node_modules/overmind-devtools-client/DevtoolBackend.js

Lines changed: 167 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,49 @@
11
const WebSocket = require('ws')
22

3-
4-
53
class DevtoolBackend {
64
constructor(options) {
75
this.options = options
8-
this.onError = this.onError.bind(this)
9-
6+
this.clientSockets = {}
107
this.onClientConnection = this.onClientConnection.bind(this)
118
this.onDevtoolConnection = this.onDevtoolConnection.bind(this)
129
this.onConnection = this.onConnection.bind(this)
1310
this.onClientMessage = this.onClientMessage.bind(this)
1411
this.onDevtoolMessage = this.onDevtoolMessage.bind(this)
15-
16-
this.devtoolServer = new WebSocket.Server(
17-
{
18-
port: options.port,
19-
},
20-
() => {}
21-
)
12+
}
13+
connect(port) {
14+
return new Promise((resolve, reject) => {
15+
this.devtoolServer = new WebSocket.Server({
16+
port,
17+
})
2218

23-
this.devtoolServer.on('connection', this.onConnection)
24-
this.devtoolServer.on('error', this.onError)
19+
this.devtoolServer.on('connection', this.onConnection)
20+
this.devtoolServer.on('error', reject)
21+
this.devtoolServer.on('listening', resolve)
22+
})
23+
}
24+
close() {
25+
this.devtoolServer.close()
2526
}
27+
getQuery(url) {
28+
return url
29+
.split('?')[1]
30+
.split(',')
31+
.reduce((aggr, part) => {
32+
const parts = part.split('=')
2633

34+
return Object.assign(aggr, {
35+
[parts[0]]: decodeURIComponent(parts[1]),
36+
})
37+
}, {})
38+
}
2739
onConnection(ws, req) {
40+
const query = this.getQuery(req.url)
41+
2842
// devtools connects with ?devtools=1 in url
29-
if (req.url.indexOf("devtools") !== -1) {
43+
if (query.devtools) {
3044
this.onDevtoolConnection(ws, req)
3145
} else {
32-
this.onClientConnection(ws, req)
46+
this.onClientConnection(query.name, ws)
3347
}
3448
}
3549

@@ -48,7 +62,10 @@ class DevtoolBackend {
4862
break
4963
case 'storage:set':
5064
this.evaluateDevtoolMessage(parsedMessage, () =>
51-
this.options.storage.set(parsedMessage.data.key, parsedMessage.data.data)
65+
this.options.storage.set(
66+
parsedMessage.data.key,
67+
parsedMessage.data.data
68+
)
5269
)
5370
break
5471
case 'storage:clear':
@@ -59,21 +76,33 @@ class DevtoolBackend {
5976
case 'relaunch':
6077
this.options.onRelaunch()
6178
break
62-
case 'connect':
63-
console.log('TODO: connect'); // TODO: Let devtools know that they are connected to server?
64-
// this.createClientServer(parsedMessage.data)
65-
break
6679
default:
67-
this.clientSocket.send(JSON.stringify(parsedMessage.data))
80+
this.clientSockets[parsedMessage.appName].send(
81+
JSON.stringify(parsedMessage.data)
82+
)
6883
}
6984
}
70-
onError(e) {
71-
console.log('TODO: onError',e);
72-
// this.devtoolServer.emit('port:exists')
73-
}
74-
onClientConnection(ws) {
75-
this.clientSocket = ws
76-
this.clientSocket.on('message', this.onClientMessage)
85+
onClientConnection(name, ws) {
86+
if (this.clientSockets[name]) {
87+
this.clientSockets[name].terminate()
88+
}
89+
90+
this.clientSockets[name] = ws
91+
92+
ws.on('message', this.onClientMessage)
93+
94+
const self = this
95+
ws.on('close', function onClose() {
96+
ws.removeEventListener('message', self.onClientMessage)
97+
ws.removeEventListener('close', onClose)
98+
99+
self.devtoolSocket.send(
100+
JSON.stringify({
101+
type: 'disconnect',
102+
data: name,
103+
})
104+
)
105+
})
77106
}
78107
onClientMessage(message) {
79108
this.devtoolSocket.send(`
@@ -98,6 +127,116 @@ class DevtoolBackend {
98127
)
99128
}
100129
}
130+
getChangePortMarkup(port, onPortSubmit, onRestart) {
131+
return `<!DOCTYPE html>
132+
<html lang="en">
133+
<head>
134+
<meta charset="UTF-8" />
135+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
136+
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
137+
<title>Document</title>
138+
<link
139+
href="https://fonts.googleapis.com/css?family=Source+Code+Pro"
140+
rel="stylesheet"
141+
/>
142+
<link
143+
href="https://fonts.googleapis.com/css?family=Nunito:400,700"
144+
rel="stylesheet"
145+
/>
146+
<script type="text/javascript">
147+
${onPortSubmit.toString()}
148+
149+
${onRestart.toString()}
150+
151+
function handleFormSubmit(event) {
152+
event.preventDefault()
153+
var input = document.querySelector('#port-input')
154+
input.setAttribute('disabled', true)
155+
${onPortSubmit.name}(input.value)
156+
}
157+
</script>
158+
<style>
159+
html, body {
160+
margin: 0;
161+
padding: 0;
162+
height: 100vh;
163+
background-color: hsl(206, 57%, 13%);
164+
}
165+
.wrapper {
166+
display: flex;
167+
align-items: center;
168+
justify-content: center;
169+
height: 100%;
170+
}
171+
.inner-wrapper {
172+
display: flex;
173+
flex-direction: column;
174+
align-items: center;
175+
}
176+
h1 {
177+
color: white;
178+
}
179+
input {
180+
border: 0;
181+
border-radius: 3px;
182+
width: 200px;
183+
padding: 1rem;
184+
font-size: 18px;
185+
outline: none;
186+
}
187+
button {
188+
padding: 1rem;
189+
border: 0;
190+
outline: 0;
191+
color: white;
192+
background-color: #fac863;
193+
margin: 1rem;
194+
font-size: 18px;
195+
border-radius: 3px;
196+
cursor: pointer;
197+
}
198+
</style>
199+
</head>
200+
<body>
201+
<div class="wrapper">
202+
<div class="inner-wrapper">
203+
<h1>The port ${port} is in use</h1>
204+
<form onsubmit="handleFormSubmit(event)">
205+
<input id="port-input" type="text" placeholder="New port..." />
206+
</form>
207+
<button onclick="${onRestart.name}()">restart</button>
208+
</div>
209+
</div>
210+
</body>
211+
</html>
212+
`
213+
}
214+
getMarkup(scriptSource, port) {
215+
return `<!DOCTYPE html>
216+
<html lang="en">
217+
<head>
218+
<meta charset="UTF-8" />
219+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
220+
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
221+
<title>Document</title>
222+
<link
223+
href="https://fonts.googleapis.com/css?family=Source+Code+Pro"
224+
rel="stylesheet"
225+
/>
226+
<link
227+
href="https://fonts.googleapis.com/css?family=Nunito:400,700"
228+
rel="stylesheet"
229+
/>
230+
<script type="text/javascript">
231+
window['__OVERMIND_DEVTOOLS_BACKEND_PORT__'] = "${port}";
232+
</script>
233+
</head>
234+
<body>
235+
<script type="text/javascript" src="${scriptSource}"></script>
236+
</body>
237+
</html>
238+
`
239+
}
101240
}
102241

103242
DevtoolBackend.create = (options) => new DevtoolBackend(options)

packages/node_modules/overmind-devtools-client/src/BackendConnector.ts

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,37 @@ class WebsocketConnector {
1212
private callbacks: {
1313
[event: string]: Function[]
1414
} = {}
15-
private socket = new WebSocket('ws://localhost:3031?devtools=1') // TODO should be able to configure
15+
private socket: WebSocket
1616
private messagesBeforeConnected: Array<[string, any, any]> = []
1717
private isOpen = false
18-
constructor() {
19-
this.socket.onmessage = (message) => {
20-
const parsedMessage = JSON.parse(message.data)
2118

22-
if (this.callbacks[parsedMessage.type]) {
23-
this.callbacks[parsedMessage.type].forEach((cb) =>
24-
cb(parsedMessage.data)
25-
)
19+
public connect(
20+
port: string | number // TODO: return Promise so we can wait for it
21+
) {
22+
return new Promise((resolve, reject) => {
23+
if (this.socket) {
24+
reject(new Error('TODO: Socked already open, what now?'))
2625
}
27-
}
28-
this.socket.onopen = () => {
29-
this.isOpen = true
30-
this.messagesBeforeConnected.forEach((message) => {
31-
this.send(...message)
32-
})
33-
this.messagesBeforeConnected.length = 0
26+
27+
this.socket = new WebSocket(`ws://localhost:${port}?devtools=1`)
28+
this.socket.onmessage = (message) => {
29+
const parsedMessage = JSON.parse(message.data)
30+
31+
this.emit(parsedMessage.type, parsedMessage.data)
32+
}
33+
this.socket.onopen = () => {
34+
resolve()
35+
this.isOpen = true
36+
this.messagesBeforeConnected.forEach((message) => {
37+
this.send(...message)
38+
})
39+
this.messagesBeforeConnected.length = 0
40+
}
41+
})
42+
}
43+
emit(event: string, data?: any) {
44+
if (this.callbacks[event]) {
45+
this.callbacks[event].forEach((cb) => cb(data))
3446
}
3547
}
3648
on(event: string, cb: Function) {
@@ -73,30 +85,11 @@ class WebsocketConnector {
7385
}
7486

7587
export class BackendConnector extends WebsocketConnector {
76-
port: string
77-
messageCallback: MessageCallback
78-
constructor() {
79-
super()
80-
this.on('port:exists', this.onPortExists)
81-
}
82-
connect(port: string) {
83-
this.send('connect', port)
84-
}
8588
onMessage = (onMessage: MessageCallback) => {
86-
this.messageCallback = onMessage
8789
this.on('message', (message) => onMessage(message))
8890
}
89-
onPortExists = (_) => {
90-
this.messageCallback({
91-
appName: null,
92-
messages: [
93-
{
94-
appName: '',
95-
type: AppMessageType.PORT_EXISTS,
96-
data: null,
97-
},
98-
],
99-
})
91+
onDisconnect = (onDisconnect: (name: string) => void) => {
92+
this.on('disconnect', (name) => onDisconnect(name))
10093
}
10194
sendMessage(appName: string, eventName: string, payload: object = null) {
10295
this.send('message', {

packages/node_modules/overmind-devtools-client/src/components/Devtools/index.tsx

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import Input from '../common/Input'
66
import Button from '../common/Button'
77
import Workspace from '../Workspace'
88
import { css } from 'emotion'
9-
import Runtime from '../Runtime'
10-
import { colors } from '../../theme'
119

1210
const Devtools: FunctionComponent = () => {
1311
const { state, actions } = useOvermind()
@@ -64,35 +62,16 @@ const Devtools: FunctionComponent = () => {
6462
)
6563
}
6664

67-
return (
68-
<Fragment>
69-
{state.isConnecting ? (
70-
<div className={styles.wrapper}>
71-
<h1>Waiting for an app to connect...</h1>
72-
<h2>or</h2>
73-
<input
74-
autoFocus
75-
disabled={state.runtimeHost && state.runtimeLoading}
76-
className={styles.input}
77-
defaultValue={
78-
state.runtimeHost ? state.runtimeHost.split('?')[0] : ''
79-
}
80-
placeholder="Connect to app..."
81-
style={{
82-
borderColor: state.runtimeError ? colors.red : 'transparent',
83-
}}
84-
onKeyDown={(event) => {
85-
if (event.keyCode === 13) {
86-
actions.setRuntimeHost(event.currentTarget.value)
87-
}
88-
}}
89-
/>
90-
</div>
91-
) : (
92-
<Workspace />
93-
)}
94-
<Runtime />
95-
</Fragment>
65+
let code = `const overmind = createOvermind(config, {
66+
devtools: "localhost:${state.port}",
67+
})`
68+
return state.isConnecting ? (
69+
<div className={styles.wrapper}>
70+
<h1>Waiting for an app to connect to {state.port}...</h1>
71+
<pre className={styles.code}>{code}</pre>
72+
</div>
73+
) : (
74+
<Workspace />
9675
)
9776
}
9877

packages/node_modules/overmind-devtools-client/src/components/Devtools/styles.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,9 @@ export const input = css({
2525
outline: 'none',
2626
borderRadius: 3,
2727
})
28+
29+
export const code = css({
30+
border: '2px dashed var(--colors-white4)',
31+
padding: '10px',
32+
background: 'var(--colors-black2)'
33+
})

0 commit comments

Comments
 (0)