Browse Source

Merge pull request #9050 from afshin/dynamic-to-federated

dynamic => federated
Afshin Taylor Darian 4 years ago
parent
commit
abf1c6349e

+ 16 - 16
dev_mode/index.js

@@ -78,14 +78,14 @@ async function main() {
   // This is all the data needed to load and activate plugins. This should be
   // gathered by the server and put onto the initial page template.
   const extension_data = JSON.parse(
-    PageConfig.getOption('dynamic_extensions')
+    PageConfig.getOption('federated_extensions')
   );
 
-  const dynamicExtensionPromises = [];
-  const dynamicMimeExtensionPromises = [];
-  const dynamicStylePromises = [];
+  const federatedExtensionPromises = [];
+  const federatedMimeExtensionPromises = [];
+  const federatedStylePromises = [];
 
-  // We first load all dynamic components so that the shared module
+  // We first load all federated components so that the shared module
   // deduplication can run and figure out which shared modules from all
   // components should be actually used.
   const extensions = await Promise.allSettled(extension_data.map( async data => {
@@ -105,13 +105,13 @@ async function main() {
 
     const data = p.value;
     if (data.extension) {
-      dynamicExtensionPromises.push(createModule(data.name, data.extension));
+      federatedExtensionPromises.push(createModule(data.name, data.extension));
     }
     if (data.mimeExtension) {
-      dynamicMimeExtensionPromises.push(createModule(data.name, data.mimeExtension));
+      federatedMimeExtensionPromises.push(createModule(data.name, data.mimeExtension));
     }
     if (data.style) {
-      dynamicStylePromises.push(createModule(data.name, data.style));
+      federatedStylePromises.push(createModule(data.name, data.style));
     }
   });
 
@@ -157,9 +157,9 @@ async function main() {
   }
   {{/each}}
 
-  // Add the dynamic mime extensions.
-  const dynamicMimeExtensions = await Promise.allSettled(dynamicMimeExtensionPromises);
-  dynamicMimeExtensions.forEach(p => {
+  // 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);
@@ -180,9 +180,9 @@ async function main() {
   }
   {{/each}}
 
-  // Add the dynamic extensions.
-  const dynamicExtensions = await Promise.allSettled(dynamicExtensionPromises);
-  dynamicExtensions.forEach(p => {
+  // Add the federated extensions.
+  const federatedExtensions = await Promise.allSettled(federatedExtensionPromises);
+  federatedExtensions.forEach(p => {
     if (p.status === "fulfilled") {
       for (let plugin of activePlugins(p.value)) {
         register.push(plugin);
@@ -192,8 +192,8 @@ async function main() {
     }
   });
 
-  // Load all dynamic component styles and log errors for any that do not
-  (await Promise.allSettled(dynamicStylePromises)).filter(({status}) => status === "rejected").forEach(({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);
   });
 

+ 8 - 6
docs/source/developer/extension_dev.rst

@@ -24,18 +24,18 @@ Starting in JupyterLab 3.0, extensions are distributed as ``pip`` or
 ``conda`` packages that contain federated JavaScript bundles.  You can write extensions in JavaScript or any language that compiles to JavaScript. We recommend writing extensions in `TypeScript <https://www.typescriptlang.org/>`_, which is used for the JupyterLab core extensions and many popular community extensions.  You use our build tool to generate the bundles that are shipped with the package, typically through a cookiecutter.
 
 
-Goals of the Dynamic Extension System
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Goals of the Federated Extension System
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 - Users should be able to install and use extensions without requiring ``node`` or a build step
 - Extension authors should be able to easily build and distribute extensions
 - The existing capabilities of built-in extensions should still work
 - Administrators should retain the ability to set global configuration and packages where possible
-- Dynamic extensions should layer on top of existing extensions similar to how  ``pip install --user`` works
+- Federated extensions should layer on top of existing extensions similar to how  ``pip install --user`` works
 - Extensions should be discoverable
 
 Implementation
 ~~~~~~~~~~~~~~
-- We provide a ``jupyter labextensions build`` script that is used to build bundles
+- We provide a ``jupyter labextension build`` script that is used to build federated bundles
    - The command produces a set of static assets that are shipped along with a package (notionally on ``pip``/``conda``)
    - It is a Python cli so that it can use the dependency metadata from the active JupyterLab
    - The assets include a module federation ``remoteEntry.*.js``, generated bundles, and some other files that we use
@@ -43,18 +43,20 @@ Implementation
    - we use the previously existing ``@jupyterlab/builder -> build`` to generate the ``imports.css``, ``schemas`` and ``themes`` file structure
 - We provide a schema for the valid ``jupyterlab`` metadata for an extension's ``package.json`` describing the available options
 - We provide a ``labextensions`` handler in ``jupyterlab_server`` that loads static assets from ``labextensions`` paths, following a similar logic to how ``nbextensions`` are discovered and loaded from disk
-- The ``settings`` and ``themes`` handlers in ``jupyterlab_server`` has been updated to load from the new ``labextensions`` locations, favoring the dynamic extension locations over the bundled ones
+- The ``settings`` and ``themes`` handlers in ``jupyterlab_server`` has been updated to load from the new ``labextensions`` locations, favoring the federated extension locations over the bundled ones
 - A ``labextension develop`` command has been added to install an in-development extension into JupyterLab.  The default behavior is to create a symlink in the ``sys-prefix/share/jupyter/labextensions/package-name`` to the static directory of the extension
 - We provide a ``cookiecutter`` that handles all of the scaffolding for an extension author, including the shipping of ``data_files`` so that when the user installs the package, the static assets end up in ``share/jupyter/labextensions``
 - We handle disabling of lab extensions using a trait on the ``LabApp`` class, so it can be set by admins and overridden by users.  Extensions are automatically enabled when installed, and must be explicitly disabled.  The disabled config can consist of a package name or a plugin regex pattern
 - Extensions can provide ``disabled`` metadata that can be used to replace an entire extension or individual plugins
 - ``page_config`` and ``overrides`` are also handled with traits so that admins can provide defaults and users can provide overrides
-- We will update the ``extension-manager`` to target metadata on ``pypi``/``conda`` and consume those packages.
+- We provide a script to update extensions: ``python -m jupyterlab.upgrade_extension``
+- We update the ``extension-manager`` to target metadata on ``pypi``/``conda`` and consume those packages.
 
 Tools
 ~~~~~
 - ``jupyter labexension build`` python command line tool
 - ``jupyter labextension develop`` python command line tool
+- ``python -m jupyterlab.upgrade_extension`` python command line tool
 - ``cookiecutter`` for extension authors
 
 Workflow for extension authors

+ 1 - 1
docs/source/developer/ui_components.rst

@@ -341,6 +341,6 @@ inline svg node as a child.
 The big limitation of the old icon-as-css-background pattern is that svg
 images rendered as ``background-image`` are invisible to CSS. On the
 other hand, an icon rendered as an inline svg node is fully exposed to
-the CSS. This allows us to dynamicly change icon styling as needed
+the CSS. This allows us to dynamically change icon styling as needed
 simply by modifying our CSS. Most importantly, this allows us to recolor
 icons according to Jupyterlab’s current theme.

+ 16 - 16
examples/federated/core_package/index.js

@@ -69,14 +69,14 @@ async function main() {
   // This is all the data needed to load and activate plugins. This should be
   // gathered by the server and put onto the initial page template.
   const extension_data = JSON.parse(
-    PageConfig.getOption('dynamic_extensions')
+    PageConfig.getOption('federated_extensions')
   );
 
-  const dynamicExtensionPromises = [];
-  const dynamicMimeExtensionPromises = [];
-  const dynamicStylePromises = [];
+  const federatedExtensionPromises = [];
+  const federatedMimeExtensionPromises = [];
+  const federatedStylePromises = [];
 
-  // We first load all dynamic components so that the shared module
+  // We first load all federated components so that the shared module
   // deduplication can run and figure out which shared modules from all
   // components should be actually used.
   const extensions = await Promise.allSettled(extension_data.map( async data => {
@@ -96,13 +96,13 @@ async function main() {
 
     const data = p.value;
     if (data.extension) {
-      dynamicExtensionPromises.push(createModule(data.name, data.extension));
+      federatedExtensionPromises.push(createModule(data.name, data.extension));
     }
     if (data.mimeExtension) {
-      dynamicMimeExtensionPromises.push(createModule(data.name, data.mimeExtension));
+      federatedMimeExtensionPromises.push(createModule(data.name, data.mimeExtension));
     }
     if (data.style) {
-      dynamicStylePromises.push(createModule(data.name, data.style));
+      federatedStylePromises.push(createModule(data.name, data.style));
     }
   });
 
@@ -148,9 +148,9 @@ async function main() {
   }
   {{/each}}
 
-  // Add the dynamic mime extensions.
-  const dynamicMimeExtensions = await Promise.allSettled(dynamicMimeExtensionPromises);
-  dynamicMimeExtensions.forEach(p => {
+  // 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);
@@ -171,9 +171,9 @@ async function main() {
   }
   {{/each}}
 
-  // Add the dynamic extensions.
-  const dynamicExtensions = await Promise.allSettled(dynamicExtensionPromises);
-  dynamicExtensions.forEach(p => {
+  // Add the federated extensions.
+  const federatedExtensions = await Promise.allSettled(federatedExtensionPromises);
+  federatedExtensions.forEach(p => {
     if (p.status === "fulfilled") {
       for (let plugin of activePlugins(p.value)) {
         register.push(plugin);
@@ -183,8 +183,8 @@ async function main() {
     }
   });
 
-  // Load all dynamic component styles and log errors for any that do not
-  (await Promise.allSettled(dynamicStylePromises)).filter(({status}) => status === "rejected").forEach(({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);
     });
 

+ 4 - 4
examples/federated/main.py

@@ -49,8 +49,8 @@ class ExampleApp(LabServerApp):
         # By default, make terminals available.
         web_app.settings.setdefault('terminals_available', True)
 
-        # Extract the dynamic extension data from lab_extensions
-        dynamic_exts = []
+        # Extract the federated extension data from lab_extensions
+        federated_exts = []
         for ext_path in [path for path in glob('./labextensions/**/package.json', recursive=True)]:
             with open(ext_path) as fid:
                 data = json.load(fid)
@@ -65,9 +65,9 @@ class ExampleApp(LabServerApp):
                 ext['mimeExtension'] = extbuild['mimeExtension']
             if 'style' in extbuild:
                 ext['style'] = extbuild['style']
-            dynamic_exts.append(ext)
+            federated_exts.append(ext)
 
-        page_config['dynamic_extensions'] = dynamic_exts
+        page_config['federated_extensions'] = federated_exts
 
         super().initialize_handlers()
 

+ 17 - 17
jupyterlab/commands.py

@@ -27,7 +27,7 @@ import warnings
 
 from jupyter_core.paths import jupyter_config_path, jupyter_path
 from jupyterlab_server.process import which, Process, WatchHelper, list2cmdline
-from jupyterlab_server.config import LabConfig, get_page_config, get_dynamic_extensions, get_static_page_config, write_page_config
+from jupyterlab_server.config import LabConfig, get_page_config, get_federated_extensions, get_static_page_config, write_page_config
 from jupyter_server.extension.serverextension import GREEN_ENABLED, GREEN_OK, RED_DISABLED, RED_X
 from traitlets import HasTraits, Bool, Dict, Instance, List, Unicode, default
 
@@ -323,7 +323,7 @@ class AppOptions(HasTraits):
 
     kill_event = Instance(Event, args=(), help='Event for aborting call')
 
-    labextensions_path = List(Unicode(), help='The paths to look in for dynamic JupyterLab extensions')
+    labextensions_path = List(Unicode(), help='The paths to look in for federated JupyterLab extensions')
 
     registry = Unicode(help="NPM packages registry URL")
 
@@ -331,7 +331,7 @@ class AppOptions(HasTraits):
     def _default_logger(self):
         return logging.getLogger('jupyterlab')
 
-    # These defaults need to be dynamic to pick up
+    # These defaults need to be federated to pick up
     # any changes to env vars:
     @default('app_dir')
     def _default_app_dir(self):
@@ -695,11 +695,11 @@ class _AppHandler(object):
 
         logger.info('JupyterLab v%s' % info['version'])
 
-        if info['dynamic_exts'] or info['extensions']:
+        if info['federated_exts'] or info['extensions']:
             info['compat_errors'] = self._get_extension_compat()
 
-        if info['dynamic_exts']:
-            self._list_dynamic_extensions()
+        if info['federated_exts']:
+            self._list_federated_extensions()
   
         if info['extensions']:
             logger.info('Other labextensions (built into JupyterLab)')
@@ -813,9 +813,9 @@ class _AppHandler(object):
         info = self.info
         logger = self.logger
 
-        # Handle dynamic extensions first
-        if name in info['dynamic_exts']:
-            data = info['dynamic_exts'].pop(name)
+        # Handle federated extensions first
+        if name in info['federated_exts']:
+            data = info['federated_exts'].pop(name)
             target = data['ext_path']
             logger.info("Removing: %s" % target)
             if os.path.isdir(target) and not os.path.islink(target):
@@ -1107,8 +1107,8 @@ class _AppHandler(object):
 
         info['disabled_core'] = disabled_core
 
-        dynamic_exts = get_dynamic_extensions(self.labextensions_path)
-        info['dynamic_exts'] = dynamic_exts
+        federated_exts = get_federated_extensions(self.labextensions_path)
+        info['federated_exts'] = federated_exts
         return info
 
     def _populate_staging(self, name=None, version=None, static_url=None,
@@ -1391,7 +1391,7 @@ class _AppHandler(object):
         compat = dict()
         core_data = self.info['core_data']
         seen = dict()
-        for (name, data) in self.info['dynamic_exts'].items():
+        for (name, data) in self.info['federated_exts'].items():
             deps = data['dependencies']
             compat[name] = _validate_compatibility(name, deps, core_data)
             seen[name] = True
@@ -1465,7 +1465,7 @@ class _AppHandler(object):
 
         logger.info('   %s dir: %s' % (ext_type, dname))
         for name in sorted(names):
-            if name in info['dynamic_exts']:
+            if name in info['federated_exts']:
                 continue
             data = info['extensions'][name]
             version = data['version']
@@ -1496,22 +1496,22 @@ class _AppHandler(object):
         # Write a blank line separator
         logger.info('')
 
-    def _list_dynamic_extensions(self):
+    def _list_federated_extensions(self):
         info = self.info
         logger = self.logger
 
         error_accumulator = {}
 
         ext_dirs = dict((p, False) for p in self.labextensions_path)
-        for value in info['dynamic_exts'].values():
+        for value in info['federated_exts'].values():
             ext_dirs[value['ext_dir']] = True
 
         for ext_dir, has_exts in ext_dirs.items():
             if not has_exts:
                 continue
             logger.info(ext_dir)
-            for name in info['dynamic_exts']:
-                data = info['dynamic_exts'][name]
+            for name in info['federated_exts']:
+                data = info['federated_exts'][name]
                 if data['ext_dir'] != ext_dir:
                     continue
                 version = data['version']

+ 5 - 5
jupyterlab/dynamic_labextensions.py → jupyterlab/federated_labextensions.py

@@ -27,7 +27,7 @@ from jupyter_core.utils import ensure_dir_exists
 from ipython_genutils.py3compat import string_types, cast_unicode_py2
 from ipython_genutils.tempdir import TemporaryDirectory
 from jupyter_server.config_manager import BaseJSONConfigManager
-from jupyterlab_server.config import get_dynamic_extensions
+from jupyterlab_server.config import get_federated_extensions
 
 from traitlets.utils.importstring import import_item
 
@@ -48,7 +48,7 @@ def develop_labextension(path, symlink=True, overwrite=False,
                         destination=None, 
                         logger=None, sys_prefix=False
                         ):
-    """Install a dynamic extension for JupyterLab
+    """Install a federated extension for JupyterLab
     
     Stages files and/or directories into the labextensions directory.
     By default, this compares modification time, and only stages files that need updating.
@@ -194,15 +194,15 @@ def watch_labextension(path, labextensions_path, logger=None, development=False,
         logger.info('Building extension in %s' % path)
 
     # Check to see if we need to create a symlink
-    dynamic_exts = get_dynamic_extensions(labextensions_path)
+    federated_exts = get_federated_extensions(labextensions_path)
 
     with open(pjoin(ext_path, 'package.json')) as fid:
         ext_data = json.load(fid)
 
-    if ext_data['name'] not in dynamic_exts:
+    if ext_data['name'] not in federated_exts:
         develop_labextension_py(ext_path, sys_prefix=True)
     else:
-        full_dest = pjoin(dynamic_exts[ext_data['name']]['ext_dir'], ext_data['name'])
+        full_dest = pjoin(federated_exts[ext_data['name']]['ext_dir'], ext_data['name'])
         output_dir = pjoin(ext_path, ext_data['jupyterlab'].get('outputDir', 'static'))
         if not osp.islink(full_dest):
             shutil.rmtree(full_dest)

+ 5 - 5
jupyterlab/labextensions.py

@@ -24,7 +24,7 @@ from .commands import (
     link_package, unlink_package, build, get_app_version, HERE,
     update_extension, AppOptions,
 )
-from .dynamic_labextensions import develop_labextension_py, build_labextension, watch_labextension
+from .federated_labextensions import develop_labextension_py, build_labextension, watch_labextension
 from .labapp import LabApp
 
 
@@ -98,7 +98,7 @@ class BaseExtensionApp(JupyterApp, DebugLogFileMixin):
     should_clean = Bool(False, config=True,
         help="Whether temporary files should be cleaned up after building jupyterlab")
 
-    labextensions_path = List(Unicode(), help='The standard paths to look in for dynamic JupyterLab extensions')
+    labextensions_path = List(Unicode(), help='The standard paths to look in for federated JupyterLab extensions')
 
     @default('labextensions_path')
     def _default_labextensions_path(self):
@@ -371,9 +371,9 @@ class CheckLabExtensionsApp(BaseExtensionApp):
 
 _examples = """
 jupyter labextension list                        # list all configured labextensions
-jupyter labextension develop                     # develop a dynamic labextension
-jupyter labextension build                       # build a dynamic labextension
-jupyter labextension watch                       # watch a dynamic labextension
+jupyter labextension develop                     # develop a federated labextension
+jupyter labextension build                       # build a federated labextension
+jupyter labextension watch                       # watch a federated labextension
 jupyter labextension install <extension name>    # install a labextension
 jupyter labextension uninstall <extension name>  # uninstall a labextension
 """

+ 1 - 1
jupyterlab/staging/index.js

@@ -79,7 +79,7 @@ async function main() {
   // This is all the data needed to load and activate plugins. This should be
   // gathered by the server and put onto the initial page template.
   const extension_data = JSON.parse(
-    PageConfig.getOption('dynamic_extensions')
+    PageConfig.getOption('federated_extensions')
   );
 
   const dynamicExtensionPromises = [];

+ 1 - 1
packages/ui-components/README.md

@@ -327,6 +327,6 @@ inline svg node as a child.
 The big limitation of the old icon-as-css-background pattern is that svg
 images rendered as `background-image` are invisible to CSS. On the other
 hand, an icon rendered as an inline svg node is fully exposed to the
-CSS. This allows us to dynamicly change icon styling as needed simply by
+CSS. This allows us to dynamically change icon styling as needed simply by
 modifying our CSS. Most importantly, this allows us to recolor icons
 according to Jupyterlab’s current theme.

+ 1 - 1
packages/ui-components/docs/source/labicon.rst

@@ -314,6 +314,6 @@ inline svg node as a child.
 The big limitation of the old icon-as-css-background pattern is that svg
 images rendered as ``background-image`` are invisible to CSS. On the
 other hand, an icon rendered as an inline svg node is fully exposed to
-the CSS. This allows us to dynamicly change icon styling as needed
+the CSS. This allows us to dynamically change icon styling as needed
 simply by modifying our CSS. Most importantly, this allows us to recolor
 icons according to Jupyterlab’s current theme.