Skip to content

Commit

Permalink
Create voilite command
Browse files Browse the repository at this point in the history
  • Loading branch information
trungleduc committed Oct 10, 2022
1 parent ee2637a commit 1063ba1
Show file tree
Hide file tree
Showing 13 changed files with 568 additions and 38 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ share/jupyter/voila/templates/reveal/static/materialcolors.css
lib

voila/labextension
voilite/static/*voilite.js
voilite/static/*.[woff|woff2|eot|svg]

tsconfig.tsbuildinfo

ui-tests/playwright-report
Expand Down
189 changes: 189 additions & 0 deletions packages/voilite/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/***************************************************************************
* Copyright (c) 2018, Voilà contributors *
* Copyright (c) 2018, QuantStack *
* *
* Distributed under the terms of the BSD 3-Clause License. *
* *
* The full license is in the file LICENSE, distributed with this software. *
****************************************************************************/

// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

// Inspired by: https://github.com/jupyterlab/jupyterlab/blob/master/dev_mode/index.js

import './style.css';

import { PageConfig, URLExt } from '@jupyterlab/coreutils';

import { VoilaApp } from '@voila-dashboards/voila';

function loadScript(url) {
return new Promise((resolve, reject) => {
const newScript = document.createElement('script');
newScript.onerror = reject;
newScript.onload = resolve;
newScript.async = true;
document.head.appendChild(newScript);
newScript.src = url;
});
}
async function loadComponent(url, scope) {
await loadScript(url);

// From MIT-licensed https://github.com/module-federation/module-federation-examples/blob/af043acd6be1718ee195b2511adf6011fba4233c/advanced-api/dynamic-remotes/app1/src/App.js#L6-L12
// eslint-disable-next-line no-undef
await __webpack_init_sharing__('default');
const container = window._JUPYTERLAB[scope];
// Initialize the container, it may provide shared modules and may need ours
// eslint-disable-next-line no-undef
await container.init(__webpack_share_scopes__.default);
}

async function createModule(scope, module) {
try {
const factory = await window._JUPYTERLAB[scope].get(module);
return factory();
} catch (e) {
console.warn(
`Failed to create module: package: ${scope}; module: ${module}`
);
throw e;
}
}

const disabled = ['@jupyter-widgets/jupyterlab-manager'];

/**
* The main function
*/
async function main() {
let mods = [
// @jupyterlab plugins
require('@jupyterlab/markdownviewer-extension'),
require('@jupyterlab/mathjax2-extension'),
require('@jupyterlab/rendermime-extension')
// TODO: add the settings endpoint to re-enable the theme plugins?
// This would also need the theme manager plugin and settings
// require('@jupyterlab/theme-light-extension'),
// require('@jupyterlab/theme-dark-extension'),
// require('./plugins')
];

const mimeExtensions = [require('@jupyterlab/json-extension')];

/**
* Iterate over active plugins in an extension.
*
* #### Notes
* This also populates the disabled
*/
function* activePlugins(extension) {
// Handle commonjs or es2015 modules
let exports;
if (Object.prototype.hasOwnProperty.call(extension, '__esModule')) {
exports = extension.default;
} else {
// CommonJS exports.
exports = extension;
}

let plugins = Array.isArray(exports) ? exports : [exports];
for (let plugin of plugins) {
if (
PageConfig.Extension.isDisabled(plugin.id) ||
disabled.includes(plugin.id) ||
disabled.includes(plugin.id.split(':')[0])
) {
continue;
}
yield plugin;
}
}

const extensionData = JSON.parse(
PageConfig.getOption('federated_extensions')
);

const federatedExtensionPromises = [];
const federatedMimeExtensionPromises = [];
const federatedStylePromises = [];

const extensions = await Promise.allSettled(
extensionData.map(async data => {
await loadComponent(
`${URLExt.join(
PageConfig.getOption('fullLabextensionsUrl'),
data.name,
data.load
)}`,
data.name
);
return data;
})
);

extensions.forEach(p => {
if (p.status === 'rejected') {
// There was an error loading the component
console.error(p.reason);
return;
}

const data = p.value;
if (data.extension) {
federatedExtensionPromises.push(createModule(data.name, data.extension));
}
if (data.mimeExtension) {
federatedMimeExtensionPromises.push(
createModule(data.name, data.mimeExtension)
);
}
if (data.style) {
federatedStylePromises.push(createModule(data.name, data.style));
}
});

// Add the federated extensions.
const federatedExtensions = await Promise.allSettled(
federatedExtensionPromises
);
federatedExtensions.forEach(p => {
if (p.status === 'fulfilled') {
for (let plugin of activePlugins(p.value)) {
mods.push(plugin);
}
} else {
console.error(p.reason);
}
});

// Add the federated mime extensions.
const federatedMimeExtensions = await Promise.allSettled(
federatedMimeExtensionPromises
);
federatedMimeExtensions.forEach(p => {
if (p.status === 'fulfilled') {
for (let plugin of activePlugins(p.value)) {
mimeExtensions.push(plugin);
}
} else {
console.error(p.reason);
}
});

// Load all federated component styles and log errors for any that do not
(await Promise.allSettled(federatedStylePromises))
.filter(({ status }) => status === 'rejected')
.forEach(({ reason }) => {
console.error(reason);
});

const app = new VoilaApp({ mimeExtensions });
app.registerPluginModules(mods);
await app.start();

window.jupyterapp = app;
}

window.addEventListener('load', main);
82 changes: 82 additions & 0 deletions packages/voilite/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"name": "@voila-dashboards/voilite",
"version": "0.4.0-beta.0",
"description": "The Voilite Frontend",
"author": "Voilà contributors",
"license": "BSD-3-Clause",
"main": "lib/index.js",
"browserslist": ">0.8%, not ie 11, not op_mini all, not dead",
"dependencies": {
"@jupyter-widgets/base": "^6.0.1",
"@jupyter-widgets/controls": "^5.0.1",
"@jupyter-widgets/jupyterlab-manager": "^5.0.3",
"@jupyter-widgets/output": "^6.0.1",
"@jupyterlab/application": "^3.0.0",
"@jupyterlab/apputils": "^3.0.0",
"@jupyterlab/coreutils": "^5.0.0",
"@jupyterlab/docregistry": "^3.0.0",
"@jupyterlab/json-extension": "^3.0.0",
"@jupyterlab/logconsole": "^3.0.0",
"@jupyterlab/mainmenu": "^3.0.0",
"@jupyterlab/markdownviewer-extension": "^3.0.0",
"@jupyterlab/mathjax2-extension": "^3.0.0",
"@jupyterlab/nbformat": "^3.0.0",
"@jupyterlab/notebook": "^3.0.0",
"@jupyterlab/outputarea": "^3.0.0",
"@jupyterlab/rendermime": "^3.0.0",
"@jupyterlab/rendermime-extension": "^3.0.0",
"@jupyterlab/services": "^6.1.8",
"@jupyterlab/settingregistry": "^3.0.0",
"@jupyterlab/translation": "^3.0.0",
"@jupyterlab/ui-components": "^3.0.0",
"@lumino/algorithm": "^1.6.2",
"@lumino/commands": "^1.15.2",
"@lumino/coreutils": "^1.8.2",
"@lumino/disposable": "^1.7.2",
"@lumino/domutils": "^1.5.2",
"@lumino/dragdrop": "^1.10.2",
"@lumino/messaging": "^1.7.2",
"@lumino/properties": "^1.5.2",
"@lumino/signaling": "^1.7.2",
"@lumino/virtualdom": "^1.11.2",
"@lumino/widgets": "^1.26.2",
"react": "^17.0.1"
},
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/preset-env": "^7.3.1",
"@jupyterlab/builder": "^3.0.0",
"@types/node": "^18.8.3",
"babel-loader": "^8.0.5",
"css-loader": "~5.0.2",
"file-loader": "^6.2.0",
"fs-extra": "^9.1.0",
"glob": "~7.1.6",
"mini-css-extract-plugin": "~0.9.0",
"npm-run-all": "^4.1.5",
"p-limit": "^2.2.2",
"raw-loader": "^4.0.2",
"rimraf": "^3.0.2",
"style-loader": "^2.0.0",
"svg-url-loader": "^7.1.1",
"typescript": "~4.1.3",
"url-loader": "^4.1.1",
"watch": "^1.0.2",
"webpack": "^5.24.1",
"webpack-bundle-analyzer": "^4.4.0",
"webpack-cli": "^4.5.0",
"webpack-merge": "^5.7.3",
"whatwg-fetch": "^3.0.0"
},
"scripts": {
"build": "npm run build:lib && webpack --mode=development",
"build:lib": "tsc",
"build:prod": "npm run build:lib && webpack --mode=production",
"clean": "jlpm run clean:lib && rimraf build",
"clean:lib": "rimraf lib tsconfig.tsbuildinfo",
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "npm-run-all -p watch:*",
"watch:lib": "tsc -w",
"watch:bundle": "webpack --watch --mode=development"
}
}
37 changes: 37 additions & 0 deletions packages/voilite/publicpath.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

// We dynamically set the webpack public path based on the page config
// settings from the JupyterLab app. We copy some of the pageconfig parsing
// logic in @jupyterlab/coreutils below, since this must run before any other
// files are loaded (including @jupyterlab/coreutils).

/**
* Get global configuration data for the Jupyter application.
*
* @param name - The name of the configuration option.
*
* @returns The config value or an empty string if not found.
*
* #### Notes
* All values are treated as strings.
* For browser based applications, it is assumed that the page HTML
* includes a script tag with the id `jupyter-config-data` containing the
* configuration as valid JSON. In order to support the classic Notebook,
* we fall back on checking for `body` data of the given `name`.
*/
function getOption(name) {
let configData = Object.create(null);
// Use script tag if available.
if (typeof document !== 'undefined' && document) {
const el = document.getElementById('jupyter-config-data');

if (el) {
configData = JSON.parse(el.textContent || '{}');
}
}
return configData[name] || '';
}

// eslint-disable-next-line no-undef
__webpack_public_path__ = getOption('fullStaticUrl') + '/';
Empty file added packages/voilite/src/app.ts
Empty file.
3 changes: 3 additions & 0 deletions packages/voilite/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@import url('~@lumino/widgets/style/index.css');

@import url('~@jupyter-widgets/controls/css/widgets.built.css');
Empty file.
9 changes: 9 additions & 0 deletions packages/voilite/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfigbase",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src",
"types": ["node"]
},
"include": ["src/**/*"]
}
Loading

0 comments on commit 1063ba1

Please sign in to comment.