mirror of
https://github.com/jupyter/notebook.git
synced 2024-11-27 03:20:27 +08:00
Fix loading of plugins (#6750)
* Explicit load plugins for the settings editor * Move logic to the tree extension * Move logic to a separate plugin * Read extensions in webpack config * Rearrange package.json * read mime extensions * Template plugins per page * Cleanup * Load allPlugins for the settings editor * Expand on the comment * Expand on comment * Fix lint
This commit is contained in:
parent
0ccb87867d
commit
2e4886f31f
@ -10,6 +10,7 @@ node_modules
|
||||
coverage
|
||||
*.map.js
|
||||
*.bundle.js
|
||||
app/index.template.js
|
||||
|
||||
# jetbrains IDE stuff
|
||||
.idea/
|
||||
|
@ -8,3 +8,4 @@ node_modules
|
||||
**/labextension
|
||||
build
|
||||
CHANGELOG.md
|
||||
app/index.template.js
|
||||
|
376
app/index.js
376
app/index.js
@ -1,376 +0,0 @@
|
||||
// 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 { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
||||
|
||||
require('./style.js');
|
||||
require('./extraStyle.js');
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main function
|
||||
*/
|
||||
async function main() {
|
||||
// load extra packages
|
||||
require('@jupyterlab/celltags');
|
||||
|
||||
const mimeExtensionsMods = [
|
||||
require('@jupyterlab/javascript-extension'),
|
||||
require('@jupyterlab/json-extension'),
|
||||
require('@jupyterlab/pdf-extension'),
|
||||
require('@jupyterlab/vega5-extension')
|
||||
];
|
||||
const mimeExtensions = await Promise.all(mimeExtensionsMods);
|
||||
|
||||
const disabled = [];
|
||||
// TODO: formalize the way the set of initial extensions and plugins are specified
|
||||
let baseMods = [
|
||||
// @jupyter-notebook plugins
|
||||
require('@jupyter-notebook/application-extension'),
|
||||
require('@jupyter-notebook/console-extension'),
|
||||
require('@jupyter-notebook/docmanager-extension'),
|
||||
require('@jupyter-notebook/documentsearch-extension'),
|
||||
require('@jupyter-notebook/help-extension'),
|
||||
require('@jupyter-notebook/notebook-extension'),
|
||||
// to handle opening new tabs after creating a new terminal
|
||||
require('@jupyter-notebook/terminal-extension'),
|
||||
|
||||
// @jupyterlab plugins
|
||||
require('@jupyterlab/application-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/application-extension:commands',
|
||||
'@jupyterlab/application-extension:context-menu',
|
||||
'@jupyterlab/application-extension:faviconbusy',
|
||||
'@jupyterlab/application-extension:router',
|
||||
'@jupyterlab/application-extension:top-bar',
|
||||
'@jupyterlab/application-extension:top-spacer'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/apputils-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/apputils-extension:palette',
|
||||
'@jupyterlab/apputils-extension:sanitizer',
|
||||
'@jupyterlab/apputils-extension:settings',
|
||||
'@jupyterlab/apputils-extension:state',
|
||||
'@jupyterlab/apputils-extension:themes',
|
||||
'@jupyterlab/apputils-extension:themes-palette-menu',
|
||||
'@jupyterlab/apputils-extension:toolbar-registry'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/codemirror-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/codemirror-extension:services'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/completer-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/completer-extension:base-service',
|
||||
'@jupyterlab/completer-extension:manager'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/console-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/console-extension:completer',
|
||||
'@jupyterlab/console-extension:factory',
|
||||
'@jupyterlab/console-extension:foreign',
|
||||
'@jupyterlab/console-extension:tracker'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/docmanager-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/docmanager-extension:plugin',
|
||||
'@jupyterlab/docmanager-extension:download',
|
||||
'@jupyterlab/docmanager-extension:contexts',
|
||||
'@jupyterlab/docmanager-extension:manager'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/documentsearch-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/documentsearch-extension:plugin'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/filebrowser-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/filebrowser-extension:factory',
|
||||
'@jupyterlab/filebrowser-extension:default-file-browser'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/fileeditor-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/fileeditor-extension:plugin'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/htmlviewer-extension'),
|
||||
require('@jupyterlab/lsp-extension'),
|
||||
require('@jupyterlab/mainmenu-extension'),
|
||||
require('@jupyterlab/markedparser-extension'),
|
||||
require('@jupyterlab/mathjax2-extension'),
|
||||
require('@jupyterlab/notebook-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/notebook-extension:code-console',
|
||||
'@jupyterlab/notebook-extension:export',
|
||||
'@jupyterlab/notebook-extension:factory',
|
||||
'@jupyterlab/notebook-extension:tracker',
|
||||
'@jupyterlab/notebook-extension:widget-factory'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/rendermime-extension'),
|
||||
require('@jupyterlab/shortcuts-extension'),
|
||||
// so new terminals can be create from the menu
|
||||
require('@jupyterlab/terminal-extension'),
|
||||
require('@jupyterlab/theme-light-extension'),
|
||||
require('@jupyterlab/theme-dark-extension'),
|
||||
require('@jupyterlab/translation-extension'),
|
||||
require('@jupyterlab/ui-components-extension'),
|
||||
// Add the "Hub Control Panel" menu option when running in JupyterHub
|
||||
require('@jupyterlab/hub-extension')
|
||||
];
|
||||
|
||||
// The motivation here is to only load a specific set of plugins dependending on
|
||||
// the current page
|
||||
const page = PageConfig.getOption('notebookPage');
|
||||
switch (page) {
|
||||
case 'tree': {
|
||||
baseMods = baseMods.concat([
|
||||
require('@jupyterlab/filebrowser-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/filebrowser-extension:browser',
|
||||
'@jupyterlab/filebrowser-extension:download',
|
||||
'@jupyterlab/filebrowser-extension:file-upload-status',
|
||||
'@jupyterlab/filebrowser-extension:open-with',
|
||||
'@jupyterlab/filebrowser-extension:search',
|
||||
'@jupyterlab/filebrowser-extension:share-file'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyter-notebook/tree-extension'),
|
||||
require('@jupyterlab/running-extension'),
|
||||
require('@jupyterlab/settingeditor-extension').default.filter(
|
||||
({ id }) =>
|
||||
['@jupyterlab/settingeditor-extension:form-ui'].includes(id)
|
||||
)
|
||||
]);
|
||||
break;
|
||||
}
|
||||
case 'notebooks': {
|
||||
baseMods = baseMods.concat([
|
||||
require('@jupyterlab/celltags-extension'),
|
||||
require('@jupyterlab/cell-toolbar-extension'),
|
||||
require('@jupyterlab/debugger-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/debugger-extension:config',
|
||||
'@jupyterlab/debugger-extension:main',
|
||||
'@jupyterlab/debugger-extension:notebooks',
|
||||
'@jupyterlab/debugger-extension:service',
|
||||
'@jupyterlab/debugger-extension:sidebar',
|
||||
'@jupyterlab/debugger-extension:sources'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/notebook-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/notebook-extension:completer',
|
||||
'@jupyterlab/notebook-extension:search',
|
||||
'@jupyterlab/notebook-extension:toc',
|
||||
'@jupyterlab/notebook-extension:tools'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/toc-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/toc-extension:registry',
|
||||
'@jupyterlab/toc-extension:tracker'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/tooltip-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/tooltip-extension:manager',
|
||||
'@jupyterlab/tooltip-extension:notebooks'
|
||||
].includes(id)
|
||||
)
|
||||
]);
|
||||
break;
|
||||
}
|
||||
case 'consoles': {
|
||||
baseMods = baseMods.concat([
|
||||
require('@jupyterlab/tooltip-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/tooltip-extension:manager',
|
||||
'@jupyterlab/tooltip-extension:consoles'
|
||||
].includes(id)
|
||||
)
|
||||
]);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
baseMods = baseMods.concat([
|
||||
require('@jupyterlab/fileeditor-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/fileeditor-extension:completer',
|
||||
'@jupyterlab/fileeditor-extension:search'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/codemirror-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/codemirror-extension:commands'].includes(id)
|
||||
)
|
||||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.push(plugin.id);
|
||||
continue;
|
||||
}
|
||||
yield plugin;
|
||||
}
|
||||
}
|
||||
|
||||
const extension_data = JSON.parse(
|
||||
PageConfig.getOption('federated_extensions')
|
||||
);
|
||||
|
||||
const mods = [];
|
||||
const federatedExtensionPromises = [];
|
||||
const federatedMimeExtensionPromises = [];
|
||||
const federatedStylePromises = [];
|
||||
|
||||
const extensions = await Promise.allSettled(
|
||||
extension_data.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 && !PageConfig.Extension.isDisabled(data.name)) {
|
||||
federatedStylePromises.push(createModule(data.name, data.style));
|
||||
}
|
||||
});
|
||||
|
||||
// Add the base frontend extensions
|
||||
const baseFrontendMods = await Promise.all(baseMods);
|
||||
baseFrontendMods.forEach(p => {
|
||||
for (let plugin of activePlugins(p)) {
|
||||
mods.push(plugin);
|
||||
}
|
||||
});
|
||||
|
||||
// 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 NotebookApp = require('@jupyter-notebook/application').NotebookApp;
|
||||
const app = new NotebookApp({ mimeExtensions });
|
||||
|
||||
app.registerPluginModules(mods);
|
||||
|
||||
// Expose global app instance when in dev mode or when toggled explicitly.
|
||||
const exposeAppInBrowser =
|
||||
(PageConfig.getOption('exposeAppInBrowser') || '').toLowerCase() === 'true';
|
||||
|
||||
if (exposeAppInBrowser) {
|
||||
window.jupyterapp = app;
|
||||
}
|
||||
|
||||
await app.start();
|
||||
}
|
||||
|
||||
window.addEventListener('load', main);
|
218
app/index.template.js
Normal file
218
app/index.template.js
Normal file
@ -0,0 +1,218 @@
|
||||
// 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 { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
||||
|
||||
require('./style.js');
|
||||
require('./extraStyle.js');
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main function
|
||||
*/
|
||||
async function main() {
|
||||
const mimeExtensionsMods = [
|
||||
{{#each notebook_mime_extensions}}
|
||||
require('{{ @key }}'),
|
||||
{{/each}}
|
||||
];
|
||||
const mimeExtensions = await Promise.all(mimeExtensionsMods);
|
||||
|
||||
// Load the base plugins available on all pages
|
||||
let baseMods = [
|
||||
{{#each notebook_plugins}}
|
||||
{{#if (ispage @key '/')}}
|
||||
{{{ list_plugins }}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
];
|
||||
|
||||
const page = `/${PageConfig.getOption('notebookPage')}`;
|
||||
switch (page) {
|
||||
{{#each notebook_plugins}}
|
||||
{{#unless (ispage @key '/')}}
|
||||
// list all the other plugins grouped by page
|
||||
case '{{ @key }}': {
|
||||
baseMods = baseMods.concat([
|
||||
{{{ list_plugins }}}
|
||||
]);
|
||||
break;
|
||||
}
|
||||
{{/unless}}
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
// populate the list of disabled extensions
|
||||
const disabled = [];
|
||||
|
||||
/**
|
||||
* 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.push(plugin.id);
|
||||
continue;
|
||||
}
|
||||
yield plugin;
|
||||
}
|
||||
}
|
||||
|
||||
const extension_data = JSON.parse(
|
||||
PageConfig.getOption('federated_extensions')
|
||||
);
|
||||
|
||||
const mods = [];
|
||||
const federatedExtensionPromises = [];
|
||||
const federatedMimeExtensionPromises = [];
|
||||
const federatedStylePromises = [];
|
||||
|
||||
const extensions = await Promise.allSettled(
|
||||
extension_data.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 && !PageConfig.Extension.isDisabled(data.name)) {
|
||||
federatedStylePromises.push(createModule(data.name, data.style));
|
||||
}
|
||||
});
|
||||
|
||||
// Add the base frontend extensions
|
||||
const baseFrontendMods = await Promise.all(baseMods);
|
||||
baseFrontendMods.forEach(p => {
|
||||
for (let plugin of activePlugins(p)) {
|
||||
mods.push(plugin);
|
||||
}
|
||||
});
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
// Set the list of base notebook multi-page plugins so the app is aware of all
|
||||
// its built-in plugins even if they are not loaded on the current page.
|
||||
// For example this is useful so the Settings Editor can list the debugger
|
||||
// plugin even if the debugger is only loaded on the notebook page.
|
||||
PageConfig.setOption('allPlugins', '{{{ json notebook_plugins }}}');
|
||||
|
||||
const NotebookApp = require('@jupyter-notebook/application').NotebookApp;
|
||||
const app = new NotebookApp({ mimeExtensions });
|
||||
|
||||
app.registerPluginModules(mods);
|
||||
|
||||
// Expose global app instance when in dev mode or when toggled explicitly.
|
||||
const exposeAppInBrowser =
|
||||
(PageConfig.getOption('exposeAppInBrowser') || '').toLowerCase() === 'true';
|
||||
|
||||
if (exposeAppInBrowser) {
|
||||
window.jupyterapp = app;
|
||||
}
|
||||
|
||||
await app.start();
|
||||
}
|
||||
|
||||
window.addEventListener('load', main);
|
181
app/package.json
181
app/package.json
@ -162,6 +162,7 @@
|
||||
"css-loader": "~5.0.1",
|
||||
"fs-extra": "^8.1.0",
|
||||
"glob": "~7.1.6",
|
||||
"handlebars": "^4.7.7",
|
||||
"mini-css-extract-plugin": "~0.9.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"style-loader": "~1.0.1",
|
||||
@ -175,45 +176,145 @@
|
||||
},
|
||||
"jupyterlab": {
|
||||
"name": "Jupyter Notebook",
|
||||
"extensions": [
|
||||
"@jupyter-notebook/application-extension",
|
||||
"@jupyter-notebook/console-extension",
|
||||
"@jupyter-notebook/docmanager-extension",
|
||||
"@jupyter-notebook/documentsearch-extension",
|
||||
"@jupyter-notebook/help-extension",
|
||||
"@jupyter-notebook/notebook-extension",
|
||||
"@jupyter-notebook/terminal-extension",
|
||||
"@jupyter-notebook/tree-extension",
|
||||
"@jupyterlab/application-extension",
|
||||
"@jupyterlab/apputils-extension",
|
||||
"@jupyterlab/cell-toolbar-extension",
|
||||
"@jupyterlab/codemirror-extension",
|
||||
"@jupyterlab/completer-extension",
|
||||
"@jupyterlab/console-extension",
|
||||
"@jupyterlab/debugger-extension",
|
||||
"@jupyterlab/docmanager-extension",
|
||||
"@jupyterlab/documentsearch-extension",
|
||||
"@jupyterlab/filebrowser-extension",
|
||||
"@jupyterlab/fileeditor-extension",
|
||||
"@jupyterlab/htmlviewer-extension",
|
||||
"@jupyterlab/hub-extension",
|
||||
"@jupyterlab/lsp-extension",
|
||||
"@jupyterlab/mainmenu-extension",
|
||||
"@jupyterlab/mathjax2-extension",
|
||||
"@jupyterlab/markedparser-extension",
|
||||
"@jupyterlab/notebook-extension",
|
||||
"@jupyterlab/pdf-extension",
|
||||
"@jupyterlab/rendermime-extension",
|
||||
"@jupyterlab/running-extension",
|
||||
"@jupyterlab/settingeditor-extension",
|
||||
"@jupyterlab/shortcuts-extension",
|
||||
"@jupyterlab/terminal-extension",
|
||||
"@jupyterlab/theme-dark-extension",
|
||||
"@jupyterlab/theme-light-extension",
|
||||
"@jupyterlab/tooltip-extension",
|
||||
"@jupyterlab/translation-extension",
|
||||
"@jupyterlab/ui-components-extension"
|
||||
"mimeExtensions": {
|
||||
"@jupyterlab/javascript-extension": true,
|
||||
"@jupyterlab/json-extension": true,
|
||||
"@jupyterlab/pdf-extension": true,
|
||||
"@jupyterlab/vega5-extension": true
|
||||
},
|
||||
"plugins": {
|
||||
"/": {
|
||||
"@jupyter-notebook/application-extension": true,
|
||||
"@jupyter-notebook/console-extension": true,
|
||||
"@jupyter-notebook/docmanager-extension": true,
|
||||
"@jupyter-notebook/documentsearch-extension": true,
|
||||
"@jupyter-notebook/help-extension": true,
|
||||
"@jupyter-notebook/notebook-extension": true,
|
||||
"@jupyter-notebook/terminal-extension": true,
|
||||
"@jupyterlab/application-extension": [
|
||||
"@jupyterlab/application-extension:commands",
|
||||
"@jupyterlab/application-extension:context-menu",
|
||||
"@jupyterlab/application-extension:faviconbusy",
|
||||
"@jupyterlab/application-extension:router",
|
||||
"@jupyterlab/application-extension:top-bar",
|
||||
"@jupyterlab/application-extension:top-spacer"
|
||||
],
|
||||
"@jupyterlab/apputils-extension": [
|
||||
"@jupyterlab/apputils-extension:palette",
|
||||
"@jupyterlab/apputils-extension:sanitizer",
|
||||
"@jupyterlab/apputils-extension:settings",
|
||||
"@jupyterlab/apputils-extension:state",
|
||||
"@jupyterlab/apputils-extension:themes",
|
||||
"@jupyterlab/apputils-extension:themes-palette-menu",
|
||||
"@jupyterlab/apputils-extension:toolbar-registry"
|
||||
],
|
||||
"@jupyterlab/codemirror-extension": [
|
||||
"@jupyterlab/codemirror-extension:services"
|
||||
],
|
||||
"@jupyterlab/completer-extension": [
|
||||
"@jupyterlab/completer-extension:base-service",
|
||||
"@jupyterlab/completer-extension:manager"
|
||||
],
|
||||
"@jupyterlab/console-extension": [
|
||||
"@jupyterlab/console-extension:completer",
|
||||
"@jupyterlab/console-extension:factory",
|
||||
"@jupyterlab/console-extension:foreign",
|
||||
"@jupyterlab/console-extension:tracker"
|
||||
],
|
||||
"@jupyterlab/docmanager-extension": [
|
||||
"@jupyterlab/docmanager-extension:plugin",
|
||||
"@jupyterlab/docmanager-extension:download",
|
||||
"@jupyterlab/docmanager-extension:contexts",
|
||||
"@jupyterlab/docmanager-extension:manager"
|
||||
],
|
||||
"@jupyterlab/documentsearch-extension": [
|
||||
"@jupyterlab/documentsearch-extension:plugin"
|
||||
],
|
||||
"@jupyterlab/filebrowser-extension": [
|
||||
"@jupyterlab/filebrowser-extension:factory",
|
||||
"@jupyterlab/filebrowser-extension:default-file-browser"
|
||||
],
|
||||
"@jupyterlab/fileeditor-extension": [
|
||||
"@jupyterlab/fileeditor-extension:plugin"
|
||||
],
|
||||
"@jupyterlab/htmlviewer-extension": true,
|
||||
"@jupyterlab/lsp-extension": true,
|
||||
"@jupyterlab/mainmenu-extension": true,
|
||||
"@jupyterlab/markedparser-extension": true,
|
||||
"@jupyterlab/mathjax2-extension": true,
|
||||
"@jupyterlab/notebook-extension": [
|
||||
"@jupyterlab/notebook-extension:code-console",
|
||||
"@jupyterlab/notebook-extension:export",
|
||||
"@jupyterlab/notebook-extension:factory",
|
||||
"@jupyterlab/notebook-extension:tracker",
|
||||
"@jupyterlab/notebook-extension:widget-factory"
|
||||
],
|
||||
"@jupyterlab/rendermime-extension": true,
|
||||
"@jupyterlab/shortcuts-extension": true,
|
||||
"@jupyterlab/terminal-extension": true,
|
||||
"@jupyterlab/theme-light-extension": true,
|
||||
"@jupyterlab/theme-dark-extension": true,
|
||||
"@jupyterlab/translation-extension": true,
|
||||
"@jupyterlab/ui-components-extension": true,
|
||||
"@jupyterlab/hub-extension": true
|
||||
},
|
||||
"/tree": {
|
||||
"@jupyterlab/filebrowser-extension": [
|
||||
"@jupyterlab/filebrowser-extension:browser",
|
||||
"@jupyterlab/filebrowser-extension:download",
|
||||
"@jupyterlab/filebrowser-extension:file-upload-status",
|
||||
"@jupyterlab/filebrowser-extension:open-with",
|
||||
"@jupyterlab/filebrowser-extension:search",
|
||||
"@jupyterlab/filebrowser-extension:share-file"
|
||||
],
|
||||
"@jupyter-notebook/tree-extension": true,
|
||||
"@jupyterlab/running-extension": true,
|
||||
"@jupyterlab/settingeditor-extension": [
|
||||
"@jupyterlab/settingeditor-extension:form-ui"
|
||||
]
|
||||
},
|
||||
"/notebooks": {
|
||||
"@jupyterlab/celltags-extension": true,
|
||||
"@jupyterlab/cell-toolbar-extension": true,
|
||||
"@jupyterlab/debugger-extension": [
|
||||
"@jupyterlab/debugger-extension:config",
|
||||
"@jupyterlab/debugger-extension:main",
|
||||
"@jupyterlab/debugger-extension:notebooks",
|
||||
"@jupyterlab/debugger-extension:service",
|
||||
"@jupyterlab/debugger-extension:sidebar",
|
||||
"@jupyterlab/debugger-extension:sources"
|
||||
],
|
||||
"@jupyterlab/notebook-extension": [
|
||||
"@jupyterlab/notebook-extension:completer",
|
||||
"@jupyterlab/notebook-extension:search",
|
||||
"@jupyterlab/notebook-extension:toc",
|
||||
"@jupyterlab/notebook-extension:tools"
|
||||
],
|
||||
"@jupyterlab/toc-extension": [
|
||||
"@jupyterlab/toc-extension:registry",
|
||||
"@jupyterlab/toc-extension:tracker"
|
||||
],
|
||||
"@jupyterlab/tooltip-extension": [
|
||||
"@jupyterlab/tooltip-extension:manager",
|
||||
"@jupyterlab/tooltip-extension:notebooks"
|
||||
]
|
||||
},
|
||||
"/consoles": {
|
||||
"@jupyterlab/tooltip-extension": [
|
||||
"@jupyterlab/tooltip-extension:manager",
|
||||
"@jupyterlab/tooltip-extension:consoles"
|
||||
]
|
||||
},
|
||||
"/edit": {
|
||||
"@jupyterlab/fileeditor-extension": [
|
||||
"@jupyterlab/fileeditor-extension:completer",
|
||||
"@jupyterlab/fileeditor-extension:search"
|
||||
],
|
||||
"@jupyterlab/codemirror-extension": [
|
||||
"@jupyterlab/codemirror-extension:commands"
|
||||
]
|
||||
}
|
||||
},
|
||||
"singletonPackages": [
|
||||
"@jupyter-notebook/tree",
|
||||
"@jupyter/ydoc",
|
||||
@ -263,8 +364,6 @@
|
||||
"react",
|
||||
"react-dom",
|
||||
"yjs"
|
||||
],
|
||||
"mimeExtensions": {},
|
||||
"linkedPackages": {}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const merge = require('webpack-merge').default;
|
||||
const Handlebars = require('handlebars');
|
||||
const { ModuleFederationPlugin } = webpack.container;
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
|
||||
.BundleAnalyzerPlugin;
|
||||
@ -29,10 +30,59 @@ if (fs.existsSync(buildDir)) {
|
||||
}
|
||||
fs.ensureDirSync(buildDir);
|
||||
|
||||
// Handle the extensions.
|
||||
const { mimeExtensions, plugins } = data.jupyterlab;
|
||||
|
||||
// Create the list of extension packages from the package.json metadata
|
||||
const extensionPackages = new Set();
|
||||
Object.keys(plugins).forEach(page => {
|
||||
const pagePlugins = plugins[page];
|
||||
Object.keys(pagePlugins).forEach(name => {
|
||||
extensionPackages.add(name);
|
||||
});
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('json', function(context) {
|
||||
return JSON.stringify(context);
|
||||
});
|
||||
|
||||
// custom help to check if a page corresponds to a value
|
||||
Handlebars.registerHelper('ispage', function(key, page) {
|
||||
return key === page;
|
||||
});
|
||||
|
||||
// custom helper to load the plugins on the index page
|
||||
Handlebars.registerHelper('list_plugins', function() {
|
||||
let str = '';
|
||||
const page = this;
|
||||
Object.keys(this).forEach(extension => {
|
||||
const plugin = page[extension];
|
||||
if (plugin === true) {
|
||||
str += `require(\'${extension}\'),\n `;
|
||||
} else if (Array.isArray(plugin)) {
|
||||
const plugins = plugin.map(p => `'${p}',`).join('\n');
|
||||
str += `
|
||||
require(\'${extension}\').default.filter(({id}) => [
|
||||
${plugins}
|
||||
].includes(id)),
|
||||
`;
|
||||
}
|
||||
});
|
||||
return str;
|
||||
});
|
||||
|
||||
// Create the entry point and other assets in build directory.
|
||||
const source = fs.readFileSync('index.template.js').toString();
|
||||
const template = Handlebars.compile(source);
|
||||
const extData = {
|
||||
notebook_plugins: plugins,
|
||||
notebook_mime_extensions: mimeExtensions
|
||||
};
|
||||
const indexOut = template(extData);
|
||||
fs.writeFileSync(path.join(buildDir, 'index.js'), indexOut);
|
||||
|
||||
// Copy extra files
|
||||
const index = path.resolve(__dirname, 'index.js');
|
||||
const cssImports = path.resolve(__dirname, 'style.js');
|
||||
fs.copySync(index, path.resolve(buildDir, 'index.js'));
|
||||
fs.copySync(cssImports, path.resolve(buildDir, 'extraStyle.js'));
|
||||
|
||||
const extras = Build.ensureAssets({
|
||||
@ -47,7 +97,6 @@ const extras = Build.ensureAssets({
|
||||
function createShared(packageData) {
|
||||
// Set up module federation sharing config
|
||||
const shared = {};
|
||||
const extensionPackages = packageData.jupyterlab.extensions;
|
||||
|
||||
// Make sure any resolutions are shared
|
||||
for (let [pkg, requiredVersion] of Object.entries(packageData.resolutions)) {
|
||||
|
@ -12,6 +12,8 @@ import {
|
||||
setToolbar
|
||||
} from '@jupyterlab/apputils';
|
||||
|
||||
import { PageConfig } from '@jupyterlab/coreutils';
|
||||
|
||||
import {
|
||||
FileBrowser,
|
||||
Uploader,
|
||||
@ -104,6 +106,63 @@ const createNew: JupyterFrontEndPlugin<void> = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Plugin to load the default plugins that are loaded on all the Notebook pages
|
||||
* (tree, edit, view, etc.) so they are visible in the settings editor.
|
||||
*/
|
||||
const loadPlugins: JupyterFrontEndPlugin<void> = {
|
||||
id: '@jupyter-notebook/tree-extension:load-plugins',
|
||||
autoStart: true,
|
||||
requires: [ISettingRegistry],
|
||||
activate(app: JupyterFrontEnd, settingRegistry: ISettingRegistry) {
|
||||
const { isDisabled } = PageConfig.Extension;
|
||||
const connector = settingRegistry.connector;
|
||||
|
||||
const allPluginsOption = PageConfig.getOption('allPlugins');
|
||||
if (!allPluginsOption) {
|
||||
return;
|
||||
}
|
||||
|
||||
// build the list of plugins shipped by default on the all the notebook pages
|
||||
// this avoid explicitly loading `'all'` plugins such as the ones used
|
||||
// in JupyterLab only
|
||||
const allPlugins = JSON.parse(allPluginsOption);
|
||||
const pluginsSet = new Set<string>();
|
||||
Object.keys(allPlugins).forEach((key: string) => {
|
||||
const extensionsAndPlugins: { [key: string]: boolean | [string] } =
|
||||
allPlugins[key];
|
||||
Object.keys(extensionsAndPlugins).forEach(plugin => {
|
||||
const value = extensionsAndPlugins[plugin];
|
||||
if (typeof value === 'boolean' && value) {
|
||||
pluginsSet.add(plugin);
|
||||
} else if (Array.isArray(value)) {
|
||||
value.forEach((v: string) => {
|
||||
pluginsSet.add(v);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.restored.then(async () => {
|
||||
const plugins = await connector.list('all');
|
||||
plugins.ids.forEach(async (id: string) => {
|
||||
const [extension] = id.split(':');
|
||||
// load the plugin if it is built-in the notebook application explicitly
|
||||
// either included as an extension or as a plugin directly
|
||||
const hasPlugin = pluginsSet.has(extension) || pluginsSet.has(id);
|
||||
if (!hasPlugin || isDisabled(id) || id in settingRegistry.plugins) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await settingRegistry.load(id);
|
||||
} catch (error) {
|
||||
console.warn(`Settings failed to load for (${id})`, error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A plugin to add file browser commands for the tree view.
|
||||
*/
|
||||
@ -225,6 +284,7 @@ const notebookTreeWidget: JupyterFrontEndPlugin<INotebookTree> = {
|
||||
*/
|
||||
const plugins: JupyterFrontEndPlugin<any>[] = [
|
||||
createNew,
|
||||
loadPlugins,
|
||||
openFileBrowser,
|
||||
notebookTreeWidget
|
||||
];
|
||||
|
Loading…
Reference in New Issue
Block a user