index.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import { PageConfig, URLExt } from '@jupyterlab/coreutils';
  4. // Promise.allSettled polyfill, until our supported browsers implement it
  5. // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
  6. if (Promise.allSettled === undefined) {
  7. Promise.allSettled = promises =>
  8. Promise.all(
  9. promises.map(promise =>
  10. promise
  11. .then(value => ({
  12. status: "fulfilled",
  13. value,
  14. }), reason => ({
  15. status: "rejected",
  16. reason,
  17. }))
  18. )
  19. );
  20. }
  21. require('./style.js')
  22. function loadScript(url) {
  23. return new Promise((resolve, reject) => {
  24. const newScript = document.createElement('script');
  25. newScript.onerror = reject;
  26. newScript.onload = resolve;
  27. newScript.async = true;
  28. document.head.appendChild(newScript);
  29. newScript.src = url;
  30. });
  31. }
  32. async function loadComponent(url, scope) {
  33. await loadScript(url);
  34. // From MIT-licensed https://github.com/module-federation/module-federation-examples/blob/af043acd6be1718ee195b2511adf6011fba4233c/advanced-api/dynamic-remotes/app1/src/App.js#L6-L12
  35. await __webpack_init_sharing__('default');
  36. const container = window._JUPYTERLAB[scope];
  37. // Initialize the container, it may provide shared modules and may need ours
  38. await container.init(__webpack_share_scopes__.default);
  39. }
  40. async function createModule(scope, module) {
  41. try {
  42. const factory = await window._JUPYTERLAB[scope].get(module);
  43. return factory();
  44. } catch(e) {
  45. console.warn(`Failed to create module: package: ${scope}; module: ${module}`);
  46. throw e;
  47. }
  48. }
  49. /**
  50. * The main entry point for the application.
  51. */
  52. async function main() {
  53. var JupyterLab = require('@jupyterlab/application').JupyterLab;
  54. var disabled = [];
  55. var deferred = [];
  56. var ignorePlugins = [];
  57. var register = [];
  58. // This is all the data needed to load and activate plugins. This should be
  59. // gathered by the server and put onto the initial page template.
  60. const extension_data = JSON.parse(
  61. PageConfig.getOption('federated_extensions')
  62. );
  63. const federatedExtensionPromises = [];
  64. const federatedMimeExtensionPromises = [];
  65. const federatedStylePromises = [];
  66. // We first load all federated components so that the shared module
  67. // deduplication can run and figure out which shared modules from all
  68. // components should be actually used.
  69. const extensions = await Promise.allSettled(extension_data.map( async data => {
  70. await loadComponent(
  71. `${URLExt.join(PageConfig.getOption('fullLabextensionsUrl'), data.name, data.load)}`,
  72. data.name
  73. );
  74. return data;
  75. }));
  76. extensions.forEach(p => {
  77. if (p.status === "rejected") {
  78. // There was an error loading the component
  79. console.error(p.reason);
  80. return;
  81. }
  82. const data = p.value;
  83. if (data.extension) {
  84. federatedExtensionPromises.push(createModule(data.name, data.extension));
  85. }
  86. if (data.mimeExtension) {
  87. federatedMimeExtensionPromises.push(createModule(data.name, data.mimeExtension));
  88. }
  89. if (data.style) {
  90. federatedStylePromises.push(createModule(data.name, data.style));
  91. }
  92. });
  93. /**
  94. * Iterate over active plugins in an extension.
  95. *
  96. * #### Notes
  97. * This also populates the disabled, deferred, and ignored arrays.
  98. */
  99. function* activePlugins(extension) {
  100. // Handle commonjs or es2015 modules
  101. let exports;
  102. if (extension.hasOwnProperty('__esModule')) {
  103. exports = extension.default;
  104. } else {
  105. // CommonJS exports.
  106. exports = extension;
  107. }
  108. let plugins = Array.isArray(exports) ? exports : [exports];
  109. for (let plugin of plugins) {
  110. if (PageConfig.Extension.isDisabled(plugin.id)) {
  111. disabled.push(plugin.id);
  112. continue;
  113. }
  114. if (PageConfig.Extension.isDeferred(plugin.id)) {
  115. deferred.push(plugin.id);
  116. ignorePlugins.push(plugin.id);
  117. }
  118. yield plugin;
  119. }
  120. }
  121. // Handle the registered mime extensions.
  122. const mimeExtensions = [];
  123. {{#each jupyterlab_mime_extensions}}
  124. try {
  125. for (let plugin of activePlugins(require('{{@key}}/{{this}}'))) {
  126. mimeExtensions.push(plugin);
  127. }
  128. } catch (e) {
  129. console.error(e);
  130. }
  131. {{/each}}
  132. // Add the federated mime extensions.
  133. const federatedMimeExtensions = await Promise.allSettled(federatedMimeExtensionPromises);
  134. federatedMimeExtensions.forEach(p => {
  135. if (p.status === "fulfilled") {
  136. for (let plugin of activePlugins(p.value)) {
  137. mimeExtensions.push(plugin);
  138. }
  139. } else {
  140. console.error(p.reason);
  141. }
  142. });
  143. // Handled the registered standard extensions.
  144. {{#each jupyterlab_extensions}}
  145. try {
  146. for (let plugin of activePlugins(require('{{@key}}/{{this}}'))) {
  147. register.push(plugin);
  148. }
  149. } catch (e) {
  150. console.error(e);
  151. }
  152. {{/each}}
  153. // Add the federated extensions.
  154. const federatedExtensions = await Promise.allSettled(federatedExtensionPromises);
  155. federatedExtensions.forEach(p => {
  156. if (p.status === "fulfilled") {
  157. for (let plugin of activePlugins(p.value)) {
  158. register.push(plugin);
  159. }
  160. } else {
  161. console.error(p.reason);
  162. }
  163. });
  164. // Load all federated component styles and log errors for any that do not
  165. (await Promise.allSettled(federatedStylePromises)).filter(({status}) => status === "rejected").forEach(({reason}) => {
  166. console.error(reason);
  167. });
  168. const lab = new JupyterLab({
  169. mimeExtensions,
  170. disabled: {
  171. matches: disabled,
  172. patterns: PageConfig.Extension.disabled
  173. .map(function (val) { return val.raw; })
  174. },
  175. deferred: {
  176. matches: deferred,
  177. patterns: PageConfig.Extension.deferred
  178. .map(function (val) { return val.raw; })
  179. },
  180. });
  181. register.forEach(function(item) { lab.registerPluginModule(item); });
  182. lab.start({ ignorePlugins });
  183. lab.restored.then(() => {
  184. console.debug('Example started!');
  185. });
  186. }
  187. window.addEventListener('load', main);