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:
Jeremy Tuloup 2023-02-27 10:32:34 +01:00 committed by GitHub
parent 0ccb87867d
commit 2e4886f31f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 473 additions and 421 deletions

View File

@ -10,6 +10,7 @@ node_modules
coverage
*.map.js
*.bundle.js
app/index.template.js
# jetbrains IDE stuff
.idea/

View File

@ -8,3 +8,4 @@ node_modules
**/labextension
build
CHANGELOG.md
app/index.template.js

View File

@ -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
View 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);

View File

@ -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": {}
]
}
}

View File

@ -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)) {

View File

@ -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
];