diff --git a/packages/voila/.babelrc b/packages/voila/.babelrc deleted file mode 100644 index dcdafd8ff..000000000 --- a/packages/voila/.babelrc +++ /dev/null @@ -1,9 +0,0 @@ -{ "presets": [ - [ - "@babel/preset-env", - { - "useBuiltIns": "usage", - "corejs": 3 - } - ] -]} diff --git a/packages/voila/package.json b/packages/voila/package.json index 242496032..f4aafd809 100644 --- a/packages/voila/package.json +++ b/packages/voila/package.json @@ -12,8 +12,10 @@ "@jupyter-widgets/controls": "^1.5.0", "@jupyter-widgets/jupyterlab-manager": "^1.0.0", "@jupyter-widgets/output": "^2.0.0", + "@jupyterlab/docregistry": "^1.0.0", "@jupyterlab/coreutils": "^3.0.0", "@jupyterlab/outputarea": "^1.0.0", + "@jupyterlab/notebook": "^1.0.0", "@jupyterlab/rendermime": "^1.0.0", "@jupyterlab/services": "^4.0.0", "@phosphor/algorithm": "^1.2.0", @@ -27,7 +29,6 @@ "mathjax-full": "^3.0.0" }, "devDependencies": { - "@babel/cli": "^7.2.3", "@babel/core": "^7.2.2", "@babel/preset-env": "^7.3.1", "babel-loader": "^8.0.5", @@ -36,6 +37,7 @@ "npm-run-all": "^4.1.5", "p-limit": "^2.2.2", "style-loader": "^0.23.1", + "typescript": "~4.1.3", "url-loader": "^1.0.0", "webpack": "^4.29.3", "webpack-cli": "^3.2.3" @@ -43,12 +45,14 @@ "style": "style/index.css", "scripts": { "build": "npm run build:lib && webpack", + "build:lib": "tsc", "build:prod": "npm run build:lib && webpack --production", - "build:lib": "babel src --out-dir lib", + "clean": "jlpm run clean:lib", + "clean:lib": "rimraf lib tsconfig.tsbuildinfo", "prepare": "npm run build:lib && webpack", "test": "echo \"Error: no test specified\" && exit 1", "watch": "npm-run-all -p watch:*", - "watch:lib": "babel src --out-dir lib --watch", + "watch:lib": "tsc -w", "watch:bundle": "webpack --watch --mode=development" } } diff --git a/packages/voila/src/index.js b/packages/voila/src/index.ts similarity index 93% rename from packages/voila/src/index.js rename to packages/voila/src/index.ts index 81094070b..e33fd91f3 100644 --- a/packages/voila/src/index.js +++ b/packages/voila/src/index.ts @@ -7,7 +7,6 @@ * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ -import { PageConfig, URLExt } from '@jupyterlab/coreutils'; import '../style/index.css'; export { diff --git a/packages/voila/src/kernel.js b/packages/voila/src/kernel.ts similarity index 80% rename from packages/voila/src/kernel.js rename to packages/voila/src/kernel.ts index b95922d2f..7ec07a3aa 100644 --- a/packages/voila/src/kernel.js +++ b/packages/voila/src/kernel.ts @@ -10,12 +10,15 @@ import { Kernel, ServerConnection } from '@jupyterlab/services'; import { PageConfig } from '@jupyterlab/coreutils'; -export async function connectKernel(baseUrl, kernelId) { +export async function connectKernel( + baseUrl: string, + kernelId: string +): Promise { baseUrl = baseUrl || PageConfig.getBaseUrl(); kernelId = kernelId || PageConfig.getOption('kernelId'); const connectionInfo = ServerConnection.makeSettings({ baseUrl }); - let model = await Kernel.findById(kernelId, connectionInfo); - let kernel = await Kernel.connectTo(model, connectionInfo); + const model = await Kernel.findById(kernelId, connectionInfo); + const kernel = Kernel.connectTo(model, connectionInfo); return kernel; } diff --git a/packages/voila/src/loader.js b/packages/voila/src/loader.ts similarity index 84% rename from packages/voila/src/loader.js rename to packages/voila/src/loader.ts index 9440b5da9..33022a7c5 100644 --- a/packages/voila/src/loader.js +++ b/packages/voila/src/loader.ts @@ -7,16 +7,16 @@ * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ -let cdn = 'https://unpkg.com/'; +const cdn = 'https://unpkg.com/'; /** * Load a package using requirejs and return a promise * * @param pkg Package name or names to load */ -let requirePromise = function(pkg) { +const requirePromise = function(pkg: string[]) { return new Promise((resolve, reject) => { - let require = window.requirejs; + const require = window.requirejs; if (require === undefined) { reject('Requirejs is needed, please ensure it is loaded on the page.'); } else { @@ -25,7 +25,7 @@ let requirePromise = function(pkg) { }); }; -function moduleNameToCDNUrl(moduleName, moduleVersion) { +function moduleNameToCDNUrl(moduleName: string, moduleVersion: string) { let packageName = moduleName; let fileName = 'index'; // default filename // if a '/' is present, like 'foo/bar', packageName is changed to 'foo', and path to 'bar' @@ -56,18 +56,21 @@ function moduleNameToCDNUrl(moduleName, moduleVersion) { * * The semver range is only used with the CDN. */ -export function requireLoader(moduleName, moduleVersion) { +export function requireLoader( + moduleName: string, + moduleVersion: string +): Promise { return requirePromise([`${moduleName}`]).catch(err => { - let failedId = err.requireModules && err.requireModules[0]; + const failedId = err.requireModules && err.requireModules[0]; if (failedId) { console.log(`Falling back to ${cdn} for ${moduleName}@${moduleVersion}`); - let require = window.requirejs; + const require = window.requirejs; if (require === undefined) { throw new Error( 'Requirejs is needed, please ensure it is loaded on the page.' ); } - const conf = { paths: {} }; + const conf: { paths: { [key: string]: string } } = { paths: {} }; conf.paths[moduleName] = moduleNameToCDNUrl(moduleName, moduleVersion); require.undef(failedId); require.config(conf); diff --git a/packages/voila/src/manager.js b/packages/voila/src/manager.ts similarity index 81% rename from packages/voila/src/manager.js rename to packages/voila/src/manager.ts index c10fcf7bc..72898fc63 100644 --- a/packages/voila/src/manager.js +++ b/packages/voila/src/manager.ts @@ -19,6 +19,9 @@ import * as AppUtils from '@jupyterlab/apputils'; import * as CoreUtils from '@jupyterlab/coreutils'; import * as DocRegistry from '@jupyterlab/docregistry'; import * as OutputArea from '@jupyterlab/outputarea'; +import { DocumentRegistry } from '@jupyterlab/docregistry'; +import { INotebookModel } from '@jupyterlab/notebook'; +import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; import * as PhosphorWidget from '@phosphor/widgets'; import * as PhosphorSignaling from '@phosphor/signaling'; @@ -31,6 +34,7 @@ import { MessageLoop } from '@phosphor/messaging'; import { requireLoader } from './loader'; import { batchRateMap } from './utils'; +import { Widget } from '@phosphor/widgets'; if (typeof window !== 'undefined' && typeof window.define !== 'undefined') { window.define('@jupyter-widgets/base', base); @@ -54,7 +58,11 @@ if (typeof window !== 'undefined' && typeof window.define !== 'undefined') { const WIDGET_MIMETYPE = 'application/vnd.jupyter.widget-view+json'; export class WidgetManager extends JupyterLabManager { - constructor(context, rendermime, settings) { + constructor( + context: DocumentRegistry.IContext, + rendermime: IRenderMimeRegistry, + settings: JupyterLabManager.Settings + ) { super(context, rendermime, settings); rendermime.addFactory( { @@ -65,23 +73,26 @@ export class WidgetManager extends JupyterLabManager { 1 ); this._registerWidgets(); - this.loader = requireLoader; + this._loader = requireLoader; } - async build_widgets() { + async build_widgets(): Promise { const models = await this._build_models(); const tags = document.body.querySelectorAll( 'script[type="application/vnd.jupyter.widget-view+json"]' ); - for (let i = 0; i !== tags.length; ++i) { + tags.forEach(async viewtag => { + if (!viewtag?.parentElement) { + return; + } try { - const viewtag = tags[i]; const widgetViewObject = JSON.parse(viewtag.innerHTML); const { model_id } = widgetViewObject; const model = models[model_id]; const widgetel = document.createElement('div'); viewtag.parentElement.insertBefore(widgetel, viewtag); - const view = await this.display_model(undefined, model, { + // TODO: fix typing + await this.display_model(undefined as any, model, { el: widgetel }); } catch (error) { @@ -95,16 +106,16 @@ export class WidgetManager extends JupyterLabManager { // This workaround may not be necessary anymore with templates that make use // of progressive rendering. } - } + }); } - display_view(msg, view, options) { + async display_view(msg: any, view: any, options: any): Promise { if (options.el) { PhosphorWidget.Widget.attach(view.pWidget, options.el); } if (view.el) { view.el.setAttribute('data-voila-jupyter-widget', ''); - view.el.addEventListener('jupyterWidgetResize', e => { + view.el.addEventListener('jupyterWidgetResize', (e: Event) => { MessageLoop.postMessage( view.pWidget, PhosphorWidget.Widget.ResizeMessage.UnknownSize @@ -114,7 +125,11 @@ export class WidgetManager extends JupyterLabManager { return view.pWidget; } - async loadClass(className, moduleName, moduleVersion) { + async loadClass( + className: string, + moduleName: string, + moduleVersion: string + ): Promise { if ( moduleName === '@jupyter-widgets/base' || moduleName === '@jupyter-widgets/controls' || @@ -123,7 +138,7 @@ export class WidgetManager extends JupyterLabManager { return super.loadClass(className, moduleName, moduleVersion); } else { // TODO: code duplicate from HTMLWidgetManager, consider a refactor - return this.loader(moduleName, moduleVersion).then(module => { + return this._loader(moduleName, moduleVersion).then(module => { if (module[className]) { return module[className]; } else { @@ -140,30 +155,31 @@ export class WidgetManager extends JupyterLabManager { } } - // eslint-disable-next-line @typescript-eslint/no-empty-function - restoreWidgets(notebook) {} + restoreWidgets(notebook: INotebookModel): Promise { + return Promise.resolve(); + } - _registerWidgets() { + private _registerWidgets(): void { this.register({ name: '@jupyter-widgets/base', version: base.JUPYTER_WIDGETS_VERSION, - exports: base + exports: base as any }); this.register({ name: '@jupyter-widgets/controls', version: controls.JUPYTER_CONTROLS_VERSION, - exports: controls + exports: controls as any }); this.register({ name: '@jupyter-widgets/output', version: output.OUTPUT_WIDGET_VERSION, - exports: output + exports: output as any }); } - async _build_models() { + async _build_models(): Promise<{ [key: string]: base.WidgetModel }> { const comm_ids = await this._get_comm_info(); - const models = {}; + const models: { [key: string]: base.WidgetModel } = {}; /** * For the classical notebook, iopub_msg_rate_limit=1000 (default) * And for zmq, we are affected by the default ZMQ_SNDHWM setting of 1000 @@ -184,13 +200,13 @@ export class WidgetManager extends JupyterLabManager { await Promise.all( widgets_info.map(async widget_info => { - const state = widget_info.msg.content.data.state; + const state = (widget_info as any).msg.content.data.state; const modelPromise = this.new_model( { model_name: state._model_name, model_module: state._model_module, model_module_version: state._model_module_version, - comm: widget_info.comm + comm: (widget_info as any).comm }, state ); @@ -202,7 +218,9 @@ export class WidgetManager extends JupyterLabManager { return models; } - async _update_comm(comm) { + async _update_comm( + comm: base.IClassicComm + ): Promise<{ comm: base.IClassicComm; msg: any }> { return new Promise((resolve, reject) => { comm.on_msg(async msg => { if (msg.content.data.buffer_paths) { @@ -219,4 +237,6 @@ export class WidgetManager extends JupyterLabManager { comm.send({ method: 'request_state' }, {}); }); } + + private _loader: (name: any, version: any) => Promise; } diff --git a/packages/voila/src/mathjax.js b/packages/voila/src/mathjax.ts similarity index 91% rename from packages/voila/src/mathjax.js rename to packages/voila/src/mathjax.ts index 6a7833e16..c35b1b46d 100644 --- a/packages/voila/src/mathjax.js +++ b/packages/voila/src/mathjax.ts @@ -20,8 +20,9 @@ RegisterHTMLHandler(browserAdaptor()); // Override dynamically generated fonts in favor // of our font css that is picked up by webpack. -class emptyFont extends TeXFont {} -emptyFont.defaultFonts = {}; +class emptyFont extends TeXFont { + readonly defaultFonts = {}; +} const chtml = new CHTML({ font: new emptyFont() @@ -47,7 +48,7 @@ const html = mathjax.document(document, { OutputJax: chtml }); -export function renderMathJax() { +export function renderMathJax(): void { html .findMath() .compile() diff --git a/packages/voila/src/typings.d.ts b/packages/voila/src/typings.d.ts new file mode 100644 index 000000000..b4030638d --- /dev/null +++ b/packages/voila/src/typings.d.ts @@ -0,0 +1,5 @@ +// eslint-disable-next-line @typescript-eslint/naming-convention +declare interface Window { + define: any; + requirejs: any; +} diff --git a/packages/voila/src/utils.js b/packages/voila/src/utils.ts similarity index 81% rename from packages/voila/src/utils.js rename to packages/voila/src/utils.ts index 30f28d1bd..882ce7142 100644 --- a/packages/voila/src/utils.js +++ b/packages/voila/src/utils.ts @@ -1,14 +1,19 @@ import pLimit from 'p-limit'; -const delay = sec => new Promise(resolve => setTimeout(resolve, sec * 1000)); +const delay = (sec: number) => + new Promise(resolve => setTimeout(resolve, sec * 1000)); /** * Map a function onto a list where fn is being called at a limit of 'rate' number of calls per second. * and 'room' number of parallel calls. * Note that the minimum window at which rate is respected is room/rate seconds. */ -export const batchRateMap = (list, fn, { room, rate }) => { - var limit = pLimit(room); +export const batchRateMap = ( + list: string[], + fn: (...args: any[]) => Promise, + { room, rate }: { room: number; rate: number } +): Promise[] => { + const limit = pLimit(room); return list.map(async value => { return new Promise((valueResolve, reject) => { limit(() => { diff --git a/packages/voila/tsconfig.json b/packages/voila/tsconfig.json new file mode 100644 index 000000000..399b75b7a --- /dev/null +++ b/packages/voila/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfigbase", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src" + }, + "include": ["src/**/*"] +} diff --git a/yarn.lock b/yarn.lock index b64b204f2..e4e6a2005 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,23 +2,6 @@ # yarn lockfile v1 -"@babel/cli@^7.2.3": - version "7.12.10" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.12.10.tgz#67a1015b1cd505bde1696196febf910c4c339a48" - integrity sha512-+y4ZnePpvWs1fc/LhZRTHkTesbXkyBYuOB+5CyodZqrEuETXi3zOVfpAQIdgC3lXbHLTDG9dQosxR9BhvLKDLQ== - dependencies: - commander "^4.0.1" - convert-source-map "^1.1.0" - fs-readdir-recursive "^1.1.0" - glob "^7.0.0" - lodash "^4.17.19" - make-dir "^2.1.0" - slash "^2.0.0" - source-map "^0.5.0" - optionalDependencies: - "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents" - chokidar "^3.4.0" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" @@ -1621,7 +1604,7 @@ "@lumino/widgets" "^1.16.1" react "^17.0.1" -"@jupyterlab/docregistry@^1.2.0", "@jupyterlab/docregistry@^1.2.9": +"@jupyterlab/docregistry@^1.0.0", "@jupyterlab/docregistry@^1.2.0", "@jupyterlab/docregistry@^1.2.9": version "1.2.9" resolved "https://registry.yarnpkg.com/@jupyterlab/docregistry/-/docregistry-1.2.9.tgz#d08d2898fa2aa186239a0cc5b426844bf8706e28" integrity sha512-08vlx/kRV3kx/3S1vNohu9LSY1cs9CbYgJLAhAMXLn3gA/O6wniPMWK5gNBsOzzl30gQ6M01hsE7993Cuk2i4Q== @@ -1776,7 +1759,7 @@ dependencies: "@lumino/coreutils" "^1.5.3" -"@jupyterlab/notebook@^1.2.0": +"@jupyterlab/notebook@^1.0.0", "@jupyterlab/notebook@^1.2.0": version "1.2.10" resolved "https://registry.yarnpkg.com/@jupyterlab/notebook/-/notebook-1.2.10.tgz#5ffcf7daed78ee037f139d8d0670ca24de6ccefe" integrity sha512-6yqYS2ii4/8z7uO/q2vtGfL8hIYX07HYXv774OizSJWNv/oM//e9NTI7gL0Cu6e49XEzchFJvWEECd7EneVh1A== @@ -2928,23 +2911,6 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents": - version "2.1.8-no-fsevents" - resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz#da7c3996b8e6e19ebd14d82eaced2313e7769f9b" - integrity sha512-+nb9vWloHNNMFHjGofEam3wopE3m1yuambrrd/fnPc+lFOMB9ROTqQlche9ByFWNkdNqfSgR/kkQtQ8DzEWt2w== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - "@nodelib/fs.scandir@2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" @@ -4759,7 +4725,7 @@ chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" -chokidar@^3.4.0, chokidar@^3.4.1: +chokidar@^3.4.1: version "3.5.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== @@ -4994,11 +4960,6 @@ commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" - integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== - commander@^6.0.0, commander@^6.2.0: version "6.2.1" resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" @@ -5163,7 +5124,7 @@ conventional-recommended-bump@^5.0.0: meow "^4.0.0" q "^1.5.1" -convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -6638,11 +6599,6 @@ fs-minipass@^2.0.0: dependencies: minipass "^3.0.0" -fs-readdir-recursive@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" - integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== - fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" @@ -6870,7 +6826,7 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@~7.1.6: +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@~7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==