Skip to content

Commit 190641f

Browse files
committed
Use proper bokehjs' types in src/widgets.ts
1 parent 38d8fd5 commit 190641f

File tree

5 files changed

+363
-117
lines changed

5 files changed

+363
-117
lines changed

.eslintrc.js

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,5 @@ module.exports = {
1111
},
1212
plugins: ['@typescript-eslint'],
1313
rules: {
14-
'@typescript-eslint/naming-convention': [
15-
'warn',
16-
{
17-
'selector': 'interface',
18-
'format': ['PascalCase'],
19-
'custom': {
20-
'regex': '^I[A-Z]',
21-
'match': true
22-
}
23-
}
24-
],
25-
'@typescript-eslint/no-unused-vars': ['warn', { args: 'none' }],
26-
'@typescript-eslint/no-explicit-any': 'off',
27-
'@typescript-eslint/no-namespace': 'off',
28-
'@typescript-eslint/no-use-before-define': 'off',
29-
'@typescript-eslint/quotes': [
30-
'error',
31-
'single',
32-
{ avoidEscape: true, allowTemplateLiterals: false }
33-
],
34-
curly: ['error', 'all'],
35-
semi: ['warn', 'never'],
36-
eqeqeq: 'warn',
37-
'prefer-arrow-callback': 'error'
3814
}
39-
};
15+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"@jupyter-widgets/jupyterlab-manager": "^5.0.4"
6969
},
7070
"devDependencies": {
71+
"@bokeh/bokehjs": "~3.4.0",
7172
"@jupyterlab/builder": "^4",
7273
"@typescript-eslint/eslint-plugin": "^7.0.1",
7374
"@typescript-eslint/parser": "^7.0.1",

src/widgets.ts

Lines changed: 63 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,46 @@
1-
import { DOMWidgetModel, DOMWidgetView } from '@jupyter-widgets/base'
1+
import {DOMWidgetModel, DOMWidgetView} from "@jupyter-widgets/base"
22

3-
//import {Document, DocumentChangedEvent, ModelChangedEvent} from "document"
4-
//import {Receiver, Fragment} from "protocol/receiver"
5-
//import {keys, values} from "core/util/object"
3+
// Use only `import type`, so that all imports are erased at run time
4+
import type {Document, DocJson, Patch, DocumentChangedEvent} from "@bokeh/bokehjs/document"
5+
import type {DocumentChanged} from "@bokeh/bokehjs/document/events"
6+
import type {Receiver, Fragment} from "@bokeh/bokehjs/protocol/receiver"
7+
import type {RenderItem} from "@bokeh/bokehjs/embed/json"
8+
import type {HasProps} from "@bokeh/bokehjs/core/has_props"
9+
import type {Ref} from "@bokeh/bokehjs/core/util/refs"
10+
import type {Serializer} from "@bokeh/bokehjs/core/serialization"
11+
import type {BokehEvent} from "@bokeh/bokehjs/core/bokeh_events"
12+
import type {add_document_standalone} from "@bokeh/bokehjs/embed/standalone"
613

7-
import { name, version } from './metadata'
14+
import {name, version} from './metadata'
815

9-
function bk_require(name: string): any {
10-
return (window as any).Bokeh.require(name)
16+
declare const Bokeh: {require(name: string): unknown}
17+
18+
function bk_require<T>(name: string): T {
19+
return Bokeh.require(name) as T
1120
}
1221

13-
type DocsJson = any
14-
type RenderItem = any
15-
type Document = any
16-
type DocumentChangedEvent = any
17-
type Receiver = any
18-
type Fragment = any
19-
type HasProps = any
20-
type Ref = any
22+
declare const Jupyter: {notebook?: unknown}
23+
24+
declare function require(name: string): unknown
2125

22-
const { keys, values } = Object
26+
const {keys} = Object
2327

2428
const version_range = `^${version}`
2529

2630
export type RenderBundle = {
27-
docs_json: DocsJson
31+
docs_json: DocJson[]
2832
render_items: RenderItem[]
2933
div: string
3034
}
3135

32-
export interface DocumentChanged {
33-
event: 'jsevent'
34-
kind: string
35-
}
36-
37-
export interface ModelChanged extends DocumentChanged {
38-
event: 'jsevent'
39-
kind: 'ModelChanged'
40-
id: string
41-
new: unknown
42-
attr: string
43-
}
44-
45-
export interface MessageSent extends DocumentChanged {
46-
event: 'jsevent'
47-
kind: 'MessageSent'
48-
msg_data: {
49-
event_name: string
50-
event_values: {
51-
model: { id: string }
52-
[other: string]: any
53-
}
54-
}
55-
msg_type: string
36+
/*
37+
declare interface DocumentChanged {
38+
event: "jsevent"
5639
}
40+
*/
5741

5842
export class BokehModel extends DOMWidgetModel {
59-
defaults(): any {
43+
defaults(): {[key: string]: unknown} {
6044
return {
6145
...super.defaults(),
6246

@@ -69,7 +53,7 @@ export class BokehModel extends DOMWidgetModel {
6953
_view_module_version: version_range,
7054

7155
combine_events: false,
72-
render_bundle: {}
56+
render_bundle: {},
7357
}
7458
}
7559

@@ -82,26 +66,23 @@ export class BokehView extends DOMWidgetView {
8266
private _document: Document | null
8367
private _receiver: Receiver
8468
private _blocked: boolean
85-
private _msgs: any[]
69+
private _msgs: DocumentChanged[]
8670
private _idle: boolean
8771
private _combine: boolean
8872

89-
constructor(options?: any) {
73+
constructor(options?: unknown) {
9074
super(options)
9175
this._document = null
9276
this._blocked = false
9377
this._idle = true
9478
this._combine = true
9579
this._msgs = []
96-
const { Receiver } = bk_require('protocol/receiver')
97-
this._receiver = new Receiver()
80+
const receiver = bk_require<{Receiver: typeof Receiver}>('protocol/receiver')
81+
this._receiver = new receiver.Receiver()
9882
this.model.on('change:render_bundle', () => this.render())
99-
if (
100-
(window as any).Jupyter != null &&
101-
(window as any).Jupyter.notebook != null
102-
) {
83+
if (Jupyter?.notebook != null) {
10384
// Handle classic Jupyter notebook
104-
const events = (window as any).require('base/js/events')
85+
const events = require('base/js/events')
10586
events.on('kernel_idle.Kernel', () => this._process_msg())
10687
} else if ((this.model.widget_manager as any).context != null) {
10788
// Handle JupyterLab and Voila
@@ -117,15 +98,11 @@ export class BokehView extends DOMWidgetView {
11798
}
11899
})
119100
} else if (this.model.get('combine_events')) {
120-
console.warn(
121-
'BokehView cannot combine events because Kernel idle status cannot be determined.'
122-
)
101+
console.warn('BokehView cannot combine events because Kernel idle status cannot be determined.')
123102
this._combine = false
124103
}
125104
} else if (this.model.get('combine_events')) {
126-
console.warn(
127-
'BokehView cannot combine events because Kernel idle status cannot be determined.'
128-
)
105+
console.warn('BokehView cannot combine events because Kernel idle status cannot be determined.')
129106
this._combine = false
130107
}
131108
this.listenTo(this.model, 'msg:custom', (content, buffers) =>
@@ -143,27 +120,25 @@ export class BokehView extends DOMWidgetView {
143120

144121
render(): void {
145122
const bundle = JSON.parse(this.model.get('render_bundle'))
146-
const { docs_json, render_items, div } = bundle as RenderBundle
123+
const {docs_json, render_items, div} = bundle as RenderBundle
147124
this.el.innerHTML = div
148-
const element = this.el.children[0]
149-
const json = values(docs_json)[0]
150-
const { Document } = bk_require('document')
151-
const { add_document_standalone } = bk_require('embed/standalone')
152-
this._document = Document.from_json(json)
153-
for (const item of render_items) {
154-
const roots: { [key: string]: Element } = {}
155-
for (const root_id in item.roots) {
156-
roots[root_id] = element
157-
}
158-
add_document_standalone(this._document, element, roots)
125+
const element = this.el.children[0] as HTMLElement
126+
// assumes docs_json.length == 1 && render_items.length == 1
127+
const doc_json = docs_json[0]
128+
const render_item = render_items[0]
129+
const document = bk_require<{Document: typeof Document}>('document')
130+
const standalone = bk_require<{add_document_standalone: typeof add_document_standalone}>('embed/standalone')
131+
this._document = document.Document.from_json(doc_json)
132+
const roots: {[key: string]: HTMLElement} = {}
133+
for (const root_id in render_item.roots) {
134+
roots[root_id] = element
159135
}
160-
this._document.on_change((event: any) => this._change_event(event))
136+
standalone.add_document_standalone(this._document, element, roots)
137+
this._document.on_change((event) => this._change_event(event))
161138
}
162139

163-
_combine_events(
164-
new_msg: ModelChanged | MessageSent
165-
): (ModelChanged | MessageSent)[] {
166-
const new_msgs = []
140+
_combine_events(new_msg: DocumentChanged): DocumentChanged[] {
141+
const new_msgs: DocumentChanged[] = []
167142
for (const msg of this._msgs) {
168143
if (new_msg.kind != msg.kind) {
169144
new_msgs.push(msg)
@@ -172,11 +147,13 @@ export class BokehView extends DOMWidgetView {
172147
new_msgs.push(msg)
173148
}
174149
} else if (msg.kind == 'MessageSent' && new_msg.kind == 'MessageSent') {
150+
// assert msg.msg_type == "bokeh_event"
151+
const data = msg.msg_data as BokehEvent
152+
const new_data = new_msg.msg_data as BokehEvent
175153
if (
176-
msg.msg_data.event_values.model == null ||
177-
msg.msg_data.event_values.model.id !=
178-
new_msg.msg_data.event_values.model.id ||
179-
msg.msg_data.event_name != new_msg.msg_data.event_name
154+
data.event_values.model == null ||
155+
data.event_values.model.id != new_data.event_values.model.id ||
156+
data.event_name != new_data.event_name
180157
) {
181158
new_msgs.push(msg)
182159
}
@@ -186,7 +163,7 @@ export class BokehView extends DOMWidgetView {
186163
return new_msgs
187164
}
188165

189-
_send(msg: ModelChanged | MessageSent): void {
166+
_send(msg: DocumentChanged): void {
190167
if (!this._idle && this._combine && this.model.get('combine_events')) {
191168
// Queue event and drop previous events on same model attribute
192169
this._msgs = this._combine_events(msg)
@@ -200,21 +177,18 @@ export class BokehView extends DOMWidgetView {
200177
if (this._blocked) {
201178
return
202179
}
203-
const { Serializer } = bk_require('core/serialization')
180+
const serialization = bk_require<{Serializer: typeof Serializer}>("core/serialization")
204181
const references: Map<HasProps, Ref> = new Map()
205182
for (const model of event.document._all_models.values()) {
206183
references.set(model, model.ref())
207184
}
208-
const serializer = new Serializer({references})
209-
const event_rep = serializer.encode(event)
210-
event_rep.event = 'jsevent'
185+
const serializer = new serialization.Serializer({references})
186+
const event_rep = serializer.encode(event) as DocumentChanged & {event: "jsevent"}
187+
event_rep.event = "jsevent"
211188
this._send(event_rep)
212189
}
213190

214-
protected _consume_patch(
215-
content: { msg: 'patch'; payload?: Fragment },
216-
buffers: DataView[]
217-
): void {
191+
protected _consume_patch(content: {msg: 'patch'; payload?: Fragment}, buffers: DataView[]): void {
218192
if (this._document == null) {
219193
return
220194
}
@@ -225,7 +199,7 @@ export class BokehView extends DOMWidgetView {
225199
if (comm_msg != null && keys(comm_msg.content).length > 0) {
226200
this._blocked = true
227201
try {
228-
this._document.apply_json_patch(comm_msg.content, comm_msg.buffers)
202+
this._document.apply_json_patch(comm_msg.content as Patch, comm_msg.buffers)
229203
} finally {
230204
this._blocked = false
231205
}

tsconfig.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@
1919
"strict": true,
2020
"strictNullChecks": false,
2121
"target": "es2017",
22-
"types": []
22+
"types": [],
23+
"paths": {
24+
"@bokeh/bokehjs/*": [
25+
"./node_modules/@bokeh/bokehjs/build/js/lib/*"
26+
]
27+
}
2328
},
2429
"include": ["src/*"]
2530
}

0 commit comments

Comments
 (0)