2023-02-27 10:32:34 +01:00
// 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 ) ;
2023-12-27 10:33:45 +01:00
const instance = factory ( ) ;
instance . _ _scope _ _ = scope ;
return instance ;
2023-02-27 10:32:34 +01:00
} 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 }}' ) ,
{ { / e a c h } }
] ;
const mimeExtensions = await Promise . all ( mimeExtensionsMods ) ;
// Load the base plugins available on all pages
let baseMods = [
{ { # each notebook _plugins } }
{ { # if ( ispage @ key '/' ) } }
{ { { list _plugins } } }
{ { / i f } }
{ { / e a c h } }
] ;
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 ;
}
{ { / u n l e s s } }
{ { / e a c h } }
}
// populate the list of disabled extensions
const disabled = [ ] ;
2023-12-27 10:33:45 +01:00
const availablePlugins = [ ] ;
2023-02-27 10:32:34 +01: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 ) {
2023-12-27 10:33:45 +01:00
const isDisabled = PageConfig . Extension . isDisabled ( plugin . id ) ;
availablePlugins . push ( {
id : plugin . id ,
description : plugin . description ,
requires : plugin . requires ? ? [ ] ,
optional : plugin . optional ? ? [ ] ,
provides : plugin . provides ? ? null ,
autoStart : plugin . autoStart ,
enabled : ! isDisabled ,
extension : extension . _ _scope _ _
} ) ;
if ( isDisabled ) {
2023-02-27 10:32:34 +01:00
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 ;
2023-12-27 10:33:45 +01:00
const app = new NotebookApp ( { mimeExtensions , availablePlugins } ) ;
2023-02-27 10:32:34 +01:00
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 ) ;