2020-12-04 21:15:41 +08:00
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
2020-12-12 19:27:50 +08:00
// Inspired by: https://github.com/jupyterlab/jupyterlab/blob/master/dev_mode/index.js
2020-12-04 21:15:41 +08:00
import { PageConfig , URLExt } from '@jupyterlab/coreutils' ;
// Promise.allSettled polyfill, until our supported browsers implement it
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
if ( Promise . allSettled === undefined ) {
Promise . allSettled = promises =>
Promise . all (
promises . map ( promise =>
promise . then (
value => ( {
status : 'fulfilled' ,
value
} ) ,
reason => ( {
status : 'rejected' ,
reason
} )
)
)
) ;
}
2020-12-10 07:47:58 +08:00
require ( './style.js' ) ;
2021-02-19 16:25:59 +08:00
require ( './extraStyle.js' ) ;
2020-12-04 21:15:41 +08:00
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 ( ) {
2021-02-19 16:25:59 +08:00
// load extra packages
require ( '@jupyterlab/celltags' ) ;
2021-10-30 18:18:28 +08:00
const mimeExtensionsMods = [
2021-04-19 04:46:28 +08:00
require ( '@jupyterlab/javascript-extension' ) ,
require ( '@jupyterlab/json-extension' ) ,
require ( '@jupyterlab/pdf-extension' ) ,
require ( '@jupyterlab/vega5-extension' )
] ;
2021-10-30 18:18:28 +08:00
const mimeExtensions = await Promise . all ( mimeExtensionsMods ) ;
2020-12-05 06:07:22 +08:00
2021-01-23 01:55:29 +08:00
const disabled = [ ] ;
2020-12-05 06:07:22 +08:00
// TODO: formalize the way the set of initial extensions and plugins are specified
2021-10-08 01:38:22 +08:00
let baseMods = [
2021-05-19 21:13:00 +08:00
// @retrolab plugins
require ( '@retrolab/application-extension' ) ,
2021-08-29 19:48:35 +08:00
require ( '@retrolab/console-extension' ) ,
2021-05-19 21:13:00 +08:00
require ( '@retrolab/docmanager-extension' ) ,
require ( '@retrolab/help-extension' ) ,
require ( '@retrolab/notebook-extension' ) ,
2020-12-10 04:20:17 +08:00
// to handle opening new tabs after creating a new terminal
2021-05-19 21:13:00 +08:00
require ( '@retrolab/terminal-extension' ) ,
2020-12-08 23:59:57 +08:00
// @jupyterlab plugins
2021-06-15 06:20:29 +08:00
require ( '@jupyterlab/application-extension' ) . default . filter ( ( { id } ) =>
2021-07-22 16:20:53 +08:00
[
'@jupyterlab/application-extension:commands' ,
'@jupyterlab/application-extension:context-menu' ,
'@jupyterlab/application-extension:faviconbusy'
] . includes ( id )
2021-06-15 06:20:29 +08:00
) ,
2020-12-04 21:15:41 +08:00
require ( '@jupyterlab/apputils-extension' ) . default . filter ( ( { id } ) =>
2020-12-05 00:32:28 +08:00
[
2020-12-07 05:34:55 +08:00
'@jupyterlab/apputils-extension:palette' ,
2020-12-05 00:32:28 +08:00
'@jupyterlab/apputils-extension:settings' ,
2021-07-23 03:00:41 +08:00
'@jupyterlab/apputils-extension:state' ,
2020-12-05 00:32:28 +08:00
'@jupyterlab/apputils-extension:themes' ,
'@jupyterlab/apputils-extension:themes-palette-menu'
] . includes ( id )
2020-12-04 21:15:41 +08:00
) ,
require ( '@jupyterlab/codemirror-extension' ) . default . filter ( ( { id } ) =>
2021-01-15 21:45:01 +08:00
[
'@jupyterlab/codemirror-extension:services' ,
'@jupyterlab/codemirror-extension:codemirror'
] . includes ( id )
2020-12-04 21:15:41 +08:00
) ,
2021-01-13 17:10:05 +08:00
require ( '@jupyterlab/completer-extension' ) . default . filter ( ( { id } ) =>
[ '@jupyterlab/completer-extension:manager' ] . includes ( id )
) ,
2021-08-29 19:48:35 +08:00
require ( '@jupyterlab/console-extension' ) ,
2020-12-05 00:03:48 +08:00
require ( '@jupyterlab/docmanager-extension' ) . default . filter ( ( { id } ) =>
2021-10-30 18:29:29 +08:00
[
'@jupyterlab/docmanager-extension:plugin' ,
'@jupyterlab/docmanager-extension:download'
] . includes ( id )
2020-12-05 00:03:48 +08:00
) ,
2021-08-28 04:16:27 +08:00
require ( '@jupyterlab/docprovider-extension' ) ,
2021-10-12 04:47:05 +08:00
require ( '@jupyterlab/filebrowser-extension' ) . default . filter ( ( { id } ) =>
2021-11-01 23:06:11 +08:00
[ '@jupyterlab/filebrowser-extension:factory' ] . includes ( id )
2021-10-12 04:47:05 +08:00
) ,
2021-09-20 21:55:25 +08:00
require ( '@jupyterlab/fileeditor-extension' ) . default . filter ( ( { id } ) =>
[ '@jupyterlab/fileeditor-extension:plugin' ] . includes ( id )
) ,
2020-12-04 21:15:41 +08:00
require ( '@jupyterlab/mainmenu-extension' ) ,
2020-12-07 04:44:58 +08:00
require ( '@jupyterlab/mathjax2-extension' ) ,
2020-12-04 21:15:41 +08:00
require ( '@jupyterlab/notebook-extension' ) . default . filter ( ( { id } ) =>
[
2021-08-30 16:59:37 +08:00
'@jupyterlab/notebook-extension:code-console' ,
2020-12-04 21:15:41 +08:00
'@jupyterlab/notebook-extension:factory' ,
2020-12-08 23:46:37 +08:00
'@jupyterlab/notebook-extension:tracker' ,
'@jupyterlab/notebook-extension:widget-factory'
2020-12-04 21:15:41 +08:00
] . includes ( id )
2020-12-04 23:35:18 +08:00
) ,
2020-12-08 23:46:37 +08:00
require ( '@jupyterlab/rendermime-extension' ) ,
2020-12-05 00:32:28 +08:00
require ( '@jupyterlab/shortcuts-extension' ) ,
2020-12-10 03:55:49 +08:00
// so new terminals can be create from the menu
require ( '@jupyterlab/terminal-extension' ) ,
2020-12-05 00:32:28 +08:00
require ( '@jupyterlab/theme-light-extension' ) ,
2021-05-29 00:40:52 +08:00
require ( '@jupyterlab/theme-dark-extension' ) ,
2021-09-22 03:43:04 +08:00
require ( '@jupyterlab/translation-extension' ) ,
2021-05-29 00:40:52 +08:00
// Add the "Hub Control Panel" menu option when running in JupyterHub
require ( '@jupyterlab/hub-extension' )
2020-12-04 21:15:41 +08:00
] ;
2020-12-12 19:27:50 +08:00
// The motivation here is to only load a specific set of plugins dependending on
// the current page
2021-05-19 21:13:00 +08:00
const page = PageConfig . getOption ( 'retroPage' ) ;
2021-04-13 20:39:42 +08:00
switch ( page ) {
case 'tree' : {
2021-10-08 01:38:22 +08:00
baseMods = baseMods . concat ( [
2021-05-20 23:19:55 +08:00
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:share-file'
] . includes ( id )
) ,
2021-05-19 21:13:00 +08:00
require ( '@retrolab/tree-extension' ) ,
2021-04-13 20:39:42 +08:00
require ( '@jupyterlab/running-extension' )
] ) ;
break ;
}
case 'notebooks' : {
2021-10-08 01:38:22 +08:00
baseMods = baseMods . concat ( [
2021-04-13 20:39:42 +08:00
require ( '@jupyterlab/completer-extension' ) . default . filter ( ( { id } ) =>
[ '@jupyterlab/completer-extension:notebooks' ] . includes ( id )
) ,
require ( '@jupyterlab/tooltip-extension' ) . default . filter ( ( { id } ) =>
[
'@jupyterlab/tooltip-extension:manager' ,
'@jupyterlab/tooltip-extension:notebooks'
] . includes ( id )
)
] ) ;
break ;
}
2021-08-30 16:33:30 +08:00
case 'consoles' : {
2021-10-08 01:38:22 +08:00
baseMods = baseMods . concat ( [
2021-08-30 16:33:30 +08:00
require ( '@jupyterlab/completer-extension' ) . default . filter ( ( { id } ) =>
[ '@jupyterlab/completer-extension:consoles' ] . includes ( id )
) ,
require ( '@jupyterlab/tooltip-extension' ) . default . filter ( ( { id } ) =>
[
'@jupyterlab/tooltip-extension:manager' ,
'@jupyterlab/tooltip-extension:consoles'
] . includes ( id )
)
] ) ;
break ;
}
2021-04-13 20:39:42 +08:00
case 'edit' : {
2021-10-08 01:38:22 +08:00
baseMods = baseMods . concat ( [
2021-04-13 20:39:42 +08:00
require ( '@jupyterlab/completer-extension' ) . default . filter ( ( { id } ) =>
[ '@jupyterlab/completer-extension:files' ] . includes ( id )
) ,
2021-05-20 23:19:55 +08:00
require ( '@jupyterlab/filebrowser-extension' ) . default . filter ( ( { id } ) =>
2021-10-12 06:31:05 +08:00
[ '@jupyterlab/filebrowser-extension:browser' ] . includes ( id )
2021-04-13 20:39:42 +08:00
)
] ) ;
break ;
}
2020-12-08 23:46:37 +08:00
}
2021-01-23 01:55:29 +08:00
/ * *
* 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 ;
}
}
2020-12-04 21:15:41 +08:00
const extension _data = JSON . parse (
PageConfig . getOption ( 'federated_extensions' )
) ;
2021-10-08 01:38:22 +08:00
const mods = [ ] ;
2020-12-04 21:15:41 +08:00
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 ) {
federatedStylePromises . push ( createModule ( data . name , data . style ) ) ;
}
} ) ;
2021-10-08 01:38:22 +08:00
// Add the base frontend extensions
const baseFrontendMods = await Promise . all ( baseMods ) ;
baseFrontendMods . forEach ( p => {
for ( let plugin of activePlugins ( p ) ) {
mods . push ( plugin ) ;
}
} ) ;
2020-12-04 21:15:41 +08:00
// Add the federated extensions.
const federatedExtensions = await Promise . allSettled (
federatedExtensionPromises
) ;
federatedExtensions . forEach ( p => {
if ( p . status === 'fulfilled' ) {
2021-01-23 01:55:29 +08:00
for ( let plugin of activePlugins ( p . value ) ) {
2021-10-09 20:28:02 +08:00
mods . push ( plugin ) ;
2021-01-23 01:55:29 +08:00
}
2020-12-04 21:15:41 +08:00
} else {
console . error ( p . reason ) ;
}
} ) ;
2021-10-30 18:18:28 +08:00
// 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 ) ;
}
} ) ;
2020-12-04 21:15:41 +08:00
// 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 ) ;
} ) ;
2021-10-30 18:18:28 +08:00
const RetroApp = require ( '@retrolab/application' ) . RetroApp ;
const app = new RetroApp ( { mimeExtensions } ) ;
2021-10-08 01:47:51 +08:00
app . registerPluginModules ( mods ) ;
2020-12-04 21:15:41 +08:00
2021-09-01 21:57:19 +08:00
// Expose global app instance when in dev mode or when toggled explicitly.
const exposeAppInBrowser =
( PageConfig . getOption ( 'exposeAppInBrowser' ) || '' ) . toLowerCase ( ) === 'true' ;
if ( exposeAppInBrowser ) {
window . jupyterapp = app ;
}
2020-12-04 21:15:41 +08:00
await app . start ( ) ;
}
window . addEventListener ( 'load' , main ) ;