extension_dev.rst 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034
  1. .. _developer_extensions:
  2. Extension Developer Guide
  3. =========================
  4. The JupyterLab application is comprised of a core application object and a set of plugins. JupyterLab plugins provide nearly every function in JupyterLab, including notebooks, document editors and viewers, code consoles, terminals, themes, the file browser, contextual help system, debugger, and settings editor. Plugins even provide more fundamental parts of the application, such as the menu system, status bar, and the underlying communication mechanism with the server.
  5. JupyterLab can be extended with several types of plugins:
  6. - **application plugins:** Application plugins are the fundamental building block of JupyterLab functionality. Application plugins interact with JupyterLab and other plugins by requiring services provided by other plugins, and optionally providing their own service to the system.
  7. - **mime renderer plugins:** Mime renderer plugins are simplified, restricted ways to extend JupyterLab to render custom mime data in notebooks and files. These plugins are automatically converted to equivalent application plugins by JupyterLab when they are loaded.
  8. - **theme plugins:** Theme plugins provide a way to customize the appearance of JupyterLab by changing themeable values (i.e., CSS variable values) and providing additional fonts and graphics to JupyterLab.
  9. Another common pattern for extending JupyterLab document widgets with application plugins is covered in :ref:`documents`.
  10. JupyterLab Extensions
  11. ---------------------
  12. Plugins are distributed in JupyterLab extensions—one extension can contain multiple plugins. An extension can be distributed in several ways:
  13. - A "source" extension is a JavaScript (npm) package that exports one or more plugins. Installing a source extension requires a user to rebuild JupyterLab. This rebuilding step requires Node.js and may take a lot of time and memory, so some users may not be able to install the extension.
  14. - A "prebuilt" extension (new in JupyterLab 3.0) is a bundle of JavaScript code that can be loaded into JupyterLab without rebuilding JupyterLab. In this case, the extension developer uses tools provided by JupyterLab to compile a source extension into a JavaScript bundle that includes the non-JupyterLab JavaScript dependencies, then distributes the resulting bundle in, for example, a Python pip or conda package. Users installing prebuilt extensions do not have to have Node.js installed and can immediately use the extension without a JupyterLab rebuild.
  15. An extension may be distributed as both a source JavaScript package published on NPM and as a prebuilt extension bundle published in a Python package, giving users the choice of how to install it.
  16. Because prebuilt extensions do not require a JupyterLab rebuild, they have a distinct advantage in multiuser systems where JuptyerLab is installed at the system level. On such systems, only the system administrator has permissions to rebuild JupyterLab and install source extensions. Since prebuilt extensions can be installed at the per-user level, the per-environment level, or the system level, each user can have their own separate set of prebuilt extensions that are loaded dynamically in their browser on top of the system-wide JupyterLab.
  17. .. tip::
  18. We recommend developing prebuilt extensions in Python packages for user convenience.
  19. Plugins interacting with each other
  20. -----------------------------------
  21. One of the foundational features of the JupyterLab plugin system is that plugins can interact with other plugins by providing a service to the system and requiring services provided by other plugins. A service can be any JavaScript value, and typically is a JavaScript object with methods and data attributes. For example, the plugin that supplies the JupyterLab main menu provides a service object to the system with methods and attributes other plugins can use to interact with the main menu.
  22. In the following discussion, the plugin that is providing a service to the system is the *provider* plugin, and the plugin that is requiring and using the service is the *consumer* plugin.
  23. A service provided by a plugin is identified by a *token*, i.e., a concrete instance of the Lumino Token class. The provider plugin lists the token in its plugin metadata ``provides`` field, and returns the associated service from its ``activate`` function. Consumer plugins import the token (for example, from the provider plugin's extension JavaScript package) and list the token in their plugin metadata ``requires`` or ``optional`` fields. When JupyterLab instantiates the consumer plugin, it will pass in the service associated with the token. JupyterLab orders plugin activation to ensure that a provider of a service is activated before its consumers.
  24. A token defined in TypeScript can also define a TypeScript interface for the service associated with the token. If the provider or consumer uses TypeScript, the service will be type-checked against this interface.
  25. .. note::
  26. JupyterLab uses tokens to identify services (instead of strings, for example) to prevent conflicts between identifiers and to enable type checking when using TypeScript.
  27. Publishing Tokens
  28. ^^^^^^^^^^^^^^^^^
  29. Since consumers will need to import a token used by a provider, the token should be exported in a published JavaScript package. A pattern in core JupyterLab is to create and export tokens from a self-contained ``tokens`` JavaScript module in a package. This enables consumers to import a token directly from the package's ``tokens`` module (e.g., ``import { MyToken } from 'provider/tokens';``), thus enabling a tree-shaking bundling optimization to bundle only the tokens and not other code from the package.
  30. Another pattern in core JupyterLab is to create and export a token from a third package that both the provider and consumer extensions import, rather than defining the token in the provider's package. This enables a user to swap out the provider extension for a different extension that provides the same token with an alternative service implementation. For example, the core JupyterLab ``filebrowser`` package exports a token representing the file browser service (enabling interactions with the file browser). The ``filebrowser-extension`` package contains a plugin that implements the file browser in JupyterLab and provides the file browser service to JupyterLab (identified with the token imported from the ``filebrowser`` package). Extensions in JupyterLab that want to interact with the filebrowser thus do not need to have a JavaScript dependency on the ``filebrowser-extension`` package, but only need to import the token from the ``filebrowser`` package. This pattern enables users to seamlessly change the file browser in JupyterLab by writing their own extension that imports the same token from the ``filebrowser`` package and provides it to the system with their own alternative file browser service.
  31. Deduplication of Dependencies
  32. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  33. ..
  34. TODO: Maybe put this part in the place where we talk about the sharedPackages metadata? It's an important implementation detail in JupyterLab that has consequences for extension metadata.
  35. One important concern and challenge in the JupyterLab extension system is deduplicating dependencies of extensions instead of having extensions use their own bundled copies of dependencies. For example, the Lumino widgets system on which JupyterLab relies for communication across the application requires all packages use the same copy of the ``@lumino/widgets`` package. Tokens identifying plugin services also need to be shared across the providers and consumers of the services, so dependencies that export tokens need to be deduplicated.
  36. Deduplication in JupyterLab happens in two ways. For source extensions, JupyterLab deduplicates dependencies when rebuilds itself to include the extension during the extension installation process. Deduplication is one of the main reasons JupyterLab needs to be rebuilt when installing source extensions. For prebuilt extensions, JupyterLab relies on the new Webpack module federation system to share dependencies across different bundles (including the core JupyterLab application bundle).
  37. To ensure that a consumer gets the same token instance as the provider provided to the sytem, any required tokens that are imported by a consumer extension should list the exporting extension as a singleton package in their ``jupyterlab.sharedPackages`` config. Required token packages should be listed as ``bundled: false`` - this will generate a JavaScript error if the package (and thus the token) is not present in the system at runtime. Optional token packages should be listed as singletons that are bundled (otherwise, if they are not present in the system, it will cause a js error when you try to import them).
  38. .. _tokens:
  39. Core Tokens
  40. ^^^^^^^^^^^
  41. The core packages of JupyterLab provide many services for plugins. The tokens
  42. for these services are listed here, along with short descriptions of when you
  43. might want to use the services in your extensions.
  44. - ``@jupyterlab/application:IConnectionLost``: A service for invoking the dialog shown
  45. when JupyterLab has lost its connection to the server. Use this if, for some reason,
  46. you want to bring up the "connection lost" dialog under new circumstances.
  47. - ``@jupyterlab/application:IInfo``: A service providing metadata about the current
  48. application, including disabled extensions and whether dev mode is enabled.
  49. - ``@jupyterlab/application:IPaths``: A service providing information about various
  50. URLs and server paths for the current application. Use this service if you want to
  51. assemble URLs to use the JupyterLab REST API.
  52. - ``@jupyterlab/application:ILabStatus``: A service for interacting with the application busy/dirty
  53. status. Use this if you want to set the application "busy" favicon, or to set
  54. the application "dirty" status, which asks the user for confirmation before leaving the application page.
  55. - ``@jupyterlab/application:ILabShell``: A service for interacting with the JupyterLab shell.
  56. The top-level ``application`` object also has a reference to the shell, but it has a restricted
  57. interface in order to be agnostic to different shell implementations on the application.
  58. Use this to get more detailed information about currently active widgets and layout state.
  59. - ``@jupyterlab/application:ILayoutRestorer``: A service providing application layout
  60. restoration functionality. Use this to have your activities restored across
  61. page loads.
  62. - ``@jupyterlab/application:IMimeDocumentTracker``: A widget tracker for documents
  63. rendered using a mime renderer extension. Use this if you want to list and interact
  64. with documents rendered by such extensions.
  65. - ``@jupyterlab/application:IRouter``: The URL router used by the application.
  66. Use this to add custom URL-routing for your extension (e.g., to invoke
  67. a command if the user navigates to a sub-path).
  68. - ``@jupyterlab/apputils:ICommandPalette``: A service for the application command palette
  69. in the left panel. Use this to add commands to the palette.
  70. - ``@jupyterlab/apputils:ISplashScreen``: A service for the splash screen for the application.
  71. Use this if you want to show the splash screen for your own purposes.
  72. - ``@jupyterlab/apputils:IThemeManager``: A service for the theme manager for the application. This is used primarily in theme extensions to register new themes.
  73. - ``@jupyterlab/apputils:IWindowResolver``: A service for a window resolver for the
  74. application. JupyterLab workspaces are given a name, which are determined using
  75. the window resolver. Require this if you want to use the name of the current workspace.
  76. - ``@jupyterlab/codeeditor:IEditorServices``: A service for the text editor provider
  77. for the application. Use this to create new text editors and host them in your
  78. UI elements.
  79. - ``@jupyterlab/completer:ICompletionManager``: A service for the completion manager
  80. for the application. Use this to allow your extension to invoke a completer.
  81. - ``@jupyterlab/console:IConsoleTracker``: A widget tracker for code consoles.
  82. Use this if you want to be able to iterate over and interact with code consoles
  83. created by the application.
  84. - ``@jupyterlab/console:IContentFactory``: A factory object that creates new code
  85. consoles. Use this if you want to create and host code consoles in your own UI elements.
  86. - ``@jupyterlab/docmanager:IDocumentManager``: A service for the manager for all
  87. documents used by the application. Use this if you want to open and close documents,
  88. create and delete files, and otherwise interact with the file system.
  89. - ``@jupyterlab/documentsearch:ISearchProviderRegistry``: A service for a registry of search
  90. providers for the application. Plugins can register their UI elements with this registry
  91. to provide find/replace support.
  92. - ``@jupyterlab/filebrowser:IFileBrowserFactory``: A factory object that creates file browsers.
  93. Use this if you want to create your own file browser (e.g., for a custom storage backend),
  94. or to interact with other file browsers that have been created by extensions.
  95. - ``@jupyterlab/fileeditor:IEditorTracker``: A widget tracker for file editors.
  96. Use this if you want to be able to iterate over and interact with file editors
  97. created by the application.
  98. - ``@jupyterlab/htmlviewer:IHTMLViewerTracker``: A widget tracker for rendered HTML documents.
  99. Use this if you want to be able to iterate over and interact with HTML documents
  100. viewed by the application.
  101. - ``@jupyterlab/imageviewer:IImageTracker``: A widget tracker for images.
  102. Use this if you want to be able to iterate over and interact with images
  103. viewed by the application.
  104. - ``@jupyterlab/inspector:IInspector``: A service for adding contextual help to widgets (visible using "Show Contextual Help" from the Help menu).
  105. Use this to hook into the contextual help system in your extension.
  106. - ``@jupyterlab/launcher:ILauncher``: A service for the application activity launcher.
  107. Use this to add your extension activities to the launcher panel.
  108. - ``@jupyterlab/mainmenu:IMainMenu``: A service for the main menu bar for the application.
  109. Use this if you want to add your own menu items or provide implementations for standardized menu items for specific activities.
  110. - ``@jupyterlab/markdownviewer:IMarkdownViewerTracker``: A widget tracker for markdown
  111. document viewers. Use this if you want to iterate over and interact with rendered markdown documents.
  112. - ``@jupyterlab/notebook:INotebookTools``: A service for the ``Notebook Tools`` panel in the
  113. right sidebar. Use this to add your own functionality to the panel.
  114. - ``@jupyterlab/notebook:IContentFactory``: A factory object that creates new notebooks.
  115. Use this if you want to create and host notebooks in your own UI elements.
  116. - ``@jupyterlab/notebook:INotebookTracker``: A widget tracker for notebooks.
  117. Use this if you want to be able to iterate over and interact with notebooks
  118. created by the application.
  119. - ``@jupyterlab/rendermime:IRenderMimeRegistry``: A service for the rendermime registry
  120. for the application. Use this to create renderers for various mime-types in your extension. Many times it will be easier to create a `mime renderer extension <#mime-renderer-extensions>`__ rather than using this service directly.
  121. - ``@jupyterlab/rendermime:ILatexTypesetter``: A service for the LaTeX typesetter for the
  122. application. Use this if you want to typeset math in your extension.
  123. - ``@jupyterlab/settingeditor:ISettingEditorTracker``: A widget tracker for setting editors.
  124. Use this if you want to be able to iterate over and interact with setting editors
  125. created by the application.
  126. - ``@jupyterlab/settingregistry:ISettingRegistry``: A service for the JupyterLab settings system.
  127. Use this if you want to store settings for your application.
  128. See `extension settings <#extension-settings>`__ for more information.
  129. - ``@jupyterlab/statedb:IStateDB``: A service for the JupyterLab state database.
  130. Use this if you want to store data that will persist across page loads.
  131. See `state database <#state-database>`__ for more information.
  132. - ``@jupyterlab/statusbar:IStatusBar``: A service for the status bar on the application.
  133. Use this if you want to add new status bar items.
  134. - ``@jupyterlab/terminal:ITerminalTracker``: A widget tracker for terminals.
  135. Use this if you want to be able to iterate over and interact with terminals
  136. created by the application.
  137. - ``@jupyterlab/tooltip:ITooltipManager``: A service for the tooltip manager for the application.
  138. Use this to allow your extension to invoke a tooltip.
  139. - ``@jupyterlab/vdom:IVDOMTracker``: A widget tracker for virtual DOM (VDOM) documents.
  140. Use this to iterate over and interact with VDOM document instances created by the application.
  141. Writing an Extension
  142. --------------------
  143. We encourage extension authors to add the `jupyterlab-extension GitHub topic
  144. <https://github.com/search?utf8=%E2%9C%93&q=topic%3Ajupyterlab-extension&type=Repositories>`__
  145. to any GitHub extension repository.
  146. Resources
  147. ^^^^^^^^^
  148. Tutorials, cookie cutters, and examples
  149. Plugin metadata
  150. ^^^^^^^^^^^^^^^
  151. A typical plugin is specified by the following metadata:
  152. .. code::
  153. const plugin: JupyterFrontEndPlugin<MyToken> = {
  154. id: 'MyExtension:my_plugin',
  155. requires: [ILabShell, ITranslator],
  156. optional: [ICommandPalette],
  157. provides: MyToken,
  158. autoStart: true
  159. activate: activateFunction
  160. };
  161. - ``id`` is a required unique string. The convention is to use the NPM extension package name and a string identifying the plugin inside the extension, separated by a colon.
  162. - ``requires`` and ``optional`` are optional lists of tokens. The corresponding objects in the system will be provided to the ``activate`` function when the plugin is instantiated. Tokens in the ``requires`` list will be required for your plugin to work, and your plugin will error if a ``required`` token is not registered with JupyterLab. Tokens in the ``optional`` list may or may not be registered, but will be provided to your plugin if they exist.
  163. - ``provides`` is the token associated with the object your plugin is providing to the system. A token can only be registered with the system once. If your plugin does not provide an object to the system, omit this field and do not return a value from your ``activate`` function.
  164. - ``autostart`` indicates whether your plugin should be activated at application startup. Typically this is ``true``. If it is ``false`` or omitted, your plugin will be instantiated when any other plugin requests the token your plugin is providing.
  165. - ``activate`` is the function called when your plugin is instantiated. The arguments are, in order, the Application object, the list of objects corresponding to the ``requires`` tokens, then the list of objects corresponding to the ``optional`` tokens (or ``null`` if that particular ``optional`` token does not exist in the system). The return value of the ``activate`` function (or resolved value if a promise is returned) is associated with the ``provides`` token in the system.
  166. package.json metadata
  167. ^^^^^^^^^^^^^^^^^^^^^
  168. Custom webpack config
  169. """""""""""""""""""""
  170. Disabling other extensions
  171. """"""""""""""""""""""""""
  172. Federation data
  173. """""""""""""""
  174. Sharing configuration
  175. """""""""""""""""""""
  176. Companion packages
  177. """"""""""""""""""
  178. Packaging extensions
  179. ^^^^^^^^^^^^^^^^^^^^
  180. Prebuilt Extensions
  181. ^^^^^^^^^^^^^^^^^^^
  182. ``install.json``
  183. How prebuilt extensions work
  184. """""""""""""""""""""""""""""
  185. Steps for building
  186. """"""""""""""""""
  187. Directory walkthrough
  188. """""""""""""""""""""
  189. Migrating extensions
  190. ^^^^^^^^^^^^^^^^^^^^
  191. Plugins
  192. -------
  193. Mime renderers
  194. ^^^^^^^^^^^^^^
  195. Theme plugins
  196. ^^^^^^^^^^^^^
  197. Advanced plugins
  198. ----------------
  199. Interacting with other plugins
  200. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  201. Common extension points
  202. """""""""""""""""""""""
  203. Settings
  204. ^^^^^^^^
  205. Menus
  206. ^^^^^
  207. Context menus
  208. ^^^^^^^^^^^^^
  209. Using React
  210. ^^^^^^^^^^^
  211. Development workflow
  212. --------------------
  213. Older Docs
  214. ==========
  215. Writing extensions
  216. ------------------
  217. Starting in JupyterLab 3.0, extensions are distributed as ``pip`` or
  218. ``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.
  219. Packaging extensions
  220. --------------------
  221. We encourage extension authors to add the `jupyterlab-extension GitHub topic
  222. <https://github.com/search?utf8=%E2%9C%93&q=topic%3Ajupyterlab-extension&type=Repositories>`__
  223. to any GitHub extension repository.
  224. Implementation
  225. --------------
  226. - We provide a ``jupyter labextension build`` script that is used to build federated bundles
  227. - The command produces a set of static assets that are shipped along with a package (notionally on ``pip``/``conda``)
  228. - It is a Python cli so that it can use the dependency metadata from the active JupyterLab
  229. - The assets include a module federation ``remoteEntry.*.js``, generated bundles, and some other files that we use
  230. - ``package.json`` is the original ``package.json`` file that we use to gather metadata about the package, with some included build metadata
  231. - we use the previously existing ``@jupyterlab/builder -> build`` to generate the ``imports.css``, ``schemas`` and ``themes`` file structure
  232. - We provide a schema for the valid ``jupyterlab`` metadata for an extension's ``package.json`` describing the available options
  233. - 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
  234. - 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
  235. - 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
  236. - 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``
  237. - 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
  238. - Extensions can provide ``disabled`` metadata that can be used to replace an entire extension or individual plugins
  239. - ``page_config`` and ``overrides`` are also handled with traits so that admins can provide defaults and users can provide overrides
  240. - We provide a script to update extensions: ``python -m jupyterlab.upgrade_extension``
  241. - We update the ``extension-manager`` to target metadata on ``pypi``/``conda`` and consume those packages.
  242. Tools
  243. -----
  244. - ``jupyter labexension build`` python command line tool
  245. - ``jupyter labextension develop`` python command line tool
  246. - ``python -m jupyterlab.upgrade_extension`` python command line tool
  247. - ``cookiecutter`` for extension authors
  248. Workflow for extension authors
  249. ------------------------------
  250. - Use the ``cookiecutter`` to create the extension
  251. - Run ``jupyter labextension develop`` to build and symlink the files
  252. - Run ``jlpm run watch`` to start watching
  253. - Run ``jupyter lab``
  254. - Make changes to source
  255. - Refresh the application page
  256. - When finished, publish the package to ``pypi``/``conda``
  257. Custom webpack config for federated extensions
  258. ----------------------------------------------
  259. .. warning::
  260. This feature is *experimental*, as it makes it possible to override the base config used by the
  261. JupyterLab Federated Extension System.
  262. It also exposes the internals of the federated extension build system (namely ``webpack``) to extension authors, which was until now
  263. kept as an implementation detail.
  264. The JupyterLab Federated Extension System uses ``webpack`` to build federated extensions, relying on the
  265. `Module Federation System <https://webpack.js.org/concepts/module-federation/>`_ added in webpack 5.
  266. To specify a custom webpack config to the federated extension build system, extension authors can add the ``webpackConfig`` subkey to the
  267. ``package.json`` of their extension::
  268. "jupyterlab": {
  269. "webpackConfig": "webpack.config.js"
  270. }
  271. The webpack config file can be placed in a different location with a custom name::
  272. "jupyterlab": {
  273. "webpackConfig": "./config/test-config.js"
  274. }
  275. Here is an example of a custom config that enables the async WebAssembly and top-level ``await`` experiments:
  276. .. code-block:: javascript
  277. module.exports = {
  278. experiments: {
  279. topLevelAwait: true,
  280. asyncWebAssembly: true,
  281. }
  282. };
  283. This custom config will be merged with the `default config <https://github.com/jupyterlab/jupyterlab/blob/master/builder/src/webpack.config.base.ts>`_
  284. when building the federated extension with ``jlpm run build``.
  285. .. note::
  286. These docs are under construction as we iterate and update tutorials and cookiecutters.
  287. Tutorials
  288. ---------
  289. We provide a set of guides to get started writing third-party extensions for JupyterLab:
  290. - :ref:`extension_tutorial`: An in-depth tutorial to learn how to make a simple JupyterLab extension.
  291. - The `JupyterLab Extension Examples Repository <https://github.com/jupyterlab/extension-examples>`_: A short tutorial series
  292. to learn how to develop extensions for JupyterLab, by example.
  293. - :ref:`developer-extension-points`: A list of the most common JupyterLab extension points.
  294. Cookiecutters
  295. -------------
  296. We provide several cookiecutters to create JupyterLab plugin extensions:
  297. - `extension-cookiecutter-ts <https://github.com/jupyterlab/extension-cookiecutter-ts>`_: Create a JupyterLab extension in TypeScript
  298. - `extension-cookiecutter-js <https://github.com/jupyterlab/extension-cookiecutter-js>`_: Create a JupyterLab extension in JavaScript
  299. - `mimerender-cookiecutter-ts <https://github.com/jupyterlab/mimerender-cookiecutter-ts>`_: Create a MIME Renderer JupyterLab extension in TypeScript
  300. - `theme-cookiecutter <https://github.com/jupyterlab/theme-cookiecutter>`_: Create a new theme for JupyterLab
  301. API Documentation
  302. -----------------
  303. If you are looking for lower level details on the JupyterLab and Lumino API:
  304. - `JupyterLab API Documentation <https://jupyterlab.github.io/jupyterlab/>`_
  305. - `Lumino API Documentation <https://jupyterlab.github.io/lumino/>`_
  306. Plugins
  307. -------
  308. A plugin adds a core functionality to the application:
  309. - A plugin can require other plugins for operation.
  310. - A plugin is activated when it is needed by other plugins, or when
  311. explicitly activated.
  312. - Plugins require and provide ``Token`` objects, which are used to
  313. provide a typed value to the plugin's ``activate()`` method.
  314. - The module providing plugin(s) must meet the
  315. `JupyterLab.IPluginModule <https://jupyterlab.github.io/jupyterlab/interfaces/_application_src_index_.jupyterlab.ipluginmodule.html>`__
  316. interface, by exporting a plugin object or array of plugin objects as
  317. the default export.
  318. The default plugins in the JupyterLab application include:
  319. - `Terminal <https://github.com/jupyterlab/jupyterlab/blob/master/packages/terminal-extension/src/index.ts>`__
  320. - Adds the ability to create command prompt terminals.
  321. - `Shortcuts <https://github.com/jupyterlab/jupyterlab/blob/master/packages/shortcuts-extension/src/index.ts>`__
  322. - Sets the default set of shortcuts for the application.
  323. - `Images <https://github.com/jupyterlab/jupyterlab/blob/master/packages/imageviewer-extension/src/index.ts>`__
  324. - Adds a widget factory for displaying image files.
  325. - `Help <https://github.com/jupyterlab/jupyterlab/blob/master/packages/help-extension/src/index.tsx>`__
  326. - Adds a side bar widget for displaying external documentation.
  327. - `File
  328. Browser <https://github.com/jupyterlab/jupyterlab/blob/master/packages/filebrowser-extension/src/index.ts>`__
  329. - Creates the file browser and the document manager and the file
  330. browser to the side bar.
  331. - `Editor <https://github.com/jupyterlab/jupyterlab/blob/master/packages/fileeditor-extension/src/index.ts>`__
  332. - Add a widget factory for displaying editable source files.
  333. - `Console <https://github.com/jupyterlab/jupyterlab/blob/master/packages/console-extension/src/index.ts>`__
  334. - Adds the ability to launch Jupyter Console instances for
  335. interactive kernel console sessions.
  336. Here is a dependency graph for the core JupyterLab components: |dependencies|
  337. .. danger::
  338. Installing an extension allows for arbitrary code execution on the
  339. server, kernel, and in the client's browser. You should therefore
  340. take steps to protect against malicious changes to your extension's
  341. code. This includes ensuring strong authentication for your PyPI
  342. account.
  343. Application Object
  344. ------------------
  345. A Jupyter front-end application object is given to each plugin in its
  346. ``activate()`` function. The application object has:
  347. - ``commands`` - an extensible registry used to add and execute commands in the application.
  348. - ``commandLinker`` - used to connect DOM nodes with the command registry so that clicking on them executes a command.
  349. - ``docRegistry`` - an extensible registry containing the document types that the application is able to read and render.
  350. - ``restored`` - a promise that is resolved when the application has finished loading.
  351. - ``serviceManager`` - low-level manager for talking to the Jupyter REST API.
  352. - ``shell`` - a generic Jupyter front-end shell instance, which holds the user interface for the application.
  353. Jupyter Front-End Shell
  354. -----------------------
  355. The Jupyter front-end
  356. `shell <https://jupyterlab.github.io/jupyterlab/interfaces/_application_src_index_.jupyterfrontend.ishell.html>`__
  357. is used to add and interact with content in the application. The ``IShell``
  358. interface provides an ``add()`` method for adding widgets to the application.
  359. In JupyterLab, the application shell consists of:
  360. - A ``top`` area for things like top-level toolbars and information.
  361. - A ``menu`` area for top-level menus, which is collapsed into the ``top`` area in multiple-document mode and put below it in single-document mode.
  362. - ``left`` and ``right`` side bar areas for collapsible content.
  363. - A ``main`` work area for user activity.
  364. - A ``bottom`` area for things like status bars.
  365. - A ``header`` area for custom elements.
  366. Lumino
  367. ------
  368. The Lumino library is used as the underlying architecture of
  369. JupyterLab and provides many of the low level primitives and widget
  370. structure used in the application. Lumino provides a rich set of
  371. widgets for developing desktop-like applications in the browser, as well
  372. as patterns and objects for writing clean, well-abstracted code. The
  373. widgets in the application are primarily **Lumino widgets**, and
  374. Lumino concepts, like message passing and signals, are used
  375. throughout. **Lumino messages** are a *many-to-one* interaction that
  376. enables information like resize events to flow through the widget
  377. hierarchy in the application. **Lumino signals** are a *one-to-many*
  378. interaction that enable listeners to react to changes in an observed
  379. object.
  380. Extension Authoring
  381. -------------------
  382. An Extension is a valid `npm
  383. package <https://docs.npmjs.com/getting-started/what-is-npm>`__ that
  384. meets the following criteria:
  385. - Exports one or more JupyterLab plugins as the default export in its
  386. main file.
  387. - Has a ``jupyterlab`` key in its ``package.json`` which has
  388. ``"extension"`` metadata. The value can be ``true`` to use the main
  389. module of the package, or a string path to a specific module (e.g.
  390. ``"lib/foo"``). Example::
  391. "jupyterlab": {
  392. "extension": true
  393. }
  394. - It is also recommended to include the keyword ``jupyterlab-extension``
  395. in the ``package.json``, to aid with discovery (e.g. by the extension
  396. manager). Example::
  397. "keywords": [
  398. "jupyter",
  399. "jupyterlab",
  400. "jupyterlab-extension"
  401. ],
  402. While authoring the extension, you can use the command:
  403. .. code:: bash
  404. npm install # install npm package dependencies
  405. npm run build # optional build step if using TypeScript, babel, etc.
  406. jupyter labextension install # install the current directory as an extension
  407. This causes the builder to re-install the source folder before building
  408. the application files. You can re-build at any time using
  409. ``jupyter lab build`` and it will reinstall these packages.
  410. You can also link other local ``npm`` packages that you are working on
  411. simultaneously using ``jupyter labextension link``; they will be re-installed
  412. but not considered as extensions. Local extensions and linked packages are
  413. included in ``jupyter labextension list``.
  414. When using local extensions and linked packages, you can run the command
  415. ::
  416. jupyter lab --watch
  417. This will cause the application to incrementally rebuild when one of the
  418. linked packages changes. Note that only compiled JavaScript files (and
  419. the CSS files) are watched by the WebPack process. This means that if
  420. your extension is in TypeScript you'll have to run a ``jlpm run build``
  421. before the changes will be reflected in JupyterLab. To avoid this step
  422. you can also watch the TypeScript sources in your extension which is
  423. usually assigned to the ``tsc -w`` shortcut. If WebPack doesn't seem to
  424. detect the changes, this can be related to `the number of available watches <https://github.com/webpack/docs/wiki/troubleshooting#not-enough-watchers>`__.
  425. Note that the application is built against **released** versions of the
  426. core JupyterLab extensions. If your extension depends on JupyterLab
  427. packages, it should be compatible with the dependencies in the
  428. ``jupyterlab/static/package.json`` file. Note that building will always use the latest JavaScript packages that meet the dependency requirements of JupyterLab itself and any installed extensions. If you wish to test against a
  429. specific patch release of one of the core JupyterLab packages you can
  430. temporarily pin that requirement to a specific version in your own
  431. dependencies.
  432. If you must install an extension into a development branch of JupyterLab, you have to graft it into the source tree of JupyterLab itself. This may be done using the command
  433. ::
  434. jlpm run add:sibling <path-or-url>
  435. in the JupyterLab root directory, where ``<path-or-url>`` refers either
  436. to an extension ``npm`` package on the local file system, or a URL to a git
  437. repository for an extension ``npm`` package. This operation may be
  438. subsequently reversed by running
  439. ::
  440. jlpm run remove:package <extension-dir-name>
  441. This will remove the package metadata from the source tree and delete
  442. all of the package files.
  443. The package should export EMCAScript 6 compatible JavaScript. It can
  444. import CSS using the syntax ``require('foo.css')``. The CSS files can
  445. also import CSS from other packages using the syntax
  446. ``@import url('~foo/index.css')``, where ``foo`` is the name of the
  447. package.
  448. The following file types are also supported (both in JavaScript and
  449. CSS): ``json``, ``html``, ``jpg``, ``png``, ``gif``, ``svg``,
  450. ``js.map``, ``woff2``, ``ttf``, ``eot``.
  451. If your package uses any other file type it must be converted to one of
  452. the above types or `include a loader in the import statement <https://webpack.js.org/concepts/loaders/#inline>`__.
  453. If you include a loader, the loader must be importable at build time, so if
  454. it is not already installed by JupyterLab, you must add it as a dependency
  455. of your extension.
  456. If your JavaScript is written in any other dialect than
  457. EMCAScript 6 (2015) it should be converted using an appropriate tool.
  458. You can use Webpack to pre-build your extension to use any of it's features
  459. not enabled in our build configuration. To build a compatible package set
  460. ``output.libraryTarget`` to ``"commonjs2"`` in your Webpack configuration.
  461. (see `this <https://github.com/saulshanabrook/jupyterlab-webpack>`__ example repo).
  462. Another option to try out your extension with a local version of JupyterLab is to add it to the
  463. list of locally installed packages and to have JupyterLab register your extension when it starts up.
  464. You can do this by adding your extension to the ``jupyterlab.externalExtensions`` key
  465. in the ``dev_mode/package.json`` file. It should be a mapping
  466. of extension name to version, just like in ``dependencies``. Then run ``jlpm run integrity``
  467. and these extensions should be added automatically to the ``dependencies`` and pulled in.
  468. When you then run ``jlpm run build && jupyter lab --dev`` or ``jupyter lab --dev --watch`` this extension
  469. will be loaded by default. For example, this is how you can add the Jupyter Widgets
  470. extensions:
  471. ::
  472. "externalExtensions": {
  473. "@jupyter-widgets/jupyterlab-manager": "2.0.0"
  474. },
  475. If you publish your extension on ``npm.org``, users will be able to install
  476. it as simply ``jupyter labextension install <foo>``, where ``<foo>`` is
  477. the name of the published ``npm`` package. You can alternatively provide a
  478. script that runs ``jupyter labextension install`` against a local folder
  479. path on the user's machine or a provided tarball. Any valid
  480. ``npm install`` specifier can be used in
  481. ``jupyter labextension install`` (e.g. ``foo@latest``, ``bar@3.0.0.0``,
  482. ``path/to/folder``, and ``path/to/tar.gz``).
  483. Testing your extension
  484. ^^^^^^^^^^^^^^^^^^^^^^
  485. There are a number of helper functions in ``testutils`` in this repo (which
  486. is a public ``npm`` package called ``@jupyterlab/testutils``) that can be used when
  487. writing tests for an extension. See ``tests/test-application`` for an example
  488. of the infrastructure needed to run tests. There is a ``karma`` config file
  489. that points to the parent directory's ``karma`` config, and a test runner,
  490. ``run-test.py`` that starts a Jupyter server.
  491. If you are using `jest <https://jestjs.io/>`__ to test your extension, you will
  492. need to transpile the jupyterlab packages to ``commonjs`` as they are using ES6 modules
  493. that ``node`` does not support.
  494. To transpile jupyterlab packages, you need to install the following package:
  495. ::
  496. jlpm add --dev jest@^24 @types/jest@^24 ts-jest@^24 @babel/core@^7 @babel/preset-env@^7
  497. Then in `jest.config.js`, you will specify to use babel for js files and ignore
  498. all node modules except the jupyterlab ones:
  499. ::
  500. module.exports = {
  501. preset: 'ts-jest/presets/js-with-babel',
  502. moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
  503. transformIgnorePatterns: ['/node_modules/(?!(@jupyterlab/.*)/)'],
  504. globals: {
  505. 'ts-jest': {
  506. tsConfig: 'tsconfig.json'
  507. }
  508. },
  509. ... // Other options useful for your extension
  510. };
  511. Finally, you will need to configure babel with a ``babel.config.js`` file containing:
  512. ::
  513. module.exports = {
  514. presets: [
  515. [
  516. '@babel/preset-env',
  517. {
  518. targets: {
  519. node: 'current'
  520. }
  521. }
  522. ]
  523. ]
  524. };
  525. .. _rendermime:
  526. Mime Renderer Extensions
  527. ------------------------
  528. Mime Renderer extensions are a convenience for creating an extension
  529. that can render mime data and potentially render files of a given type.
  530. We provide a cookiecutter for mime renderer extensions in TypeScript
  531. `here <https://github.com/jupyterlab/mimerender-cookiecutter-ts>`__.
  532. Mime renderer extensions are more declarative than standard extensions.
  533. The extension is treated the same from the command line perspective
  534. (``jupyter labextension install`` ), but it does not directly create
  535. JupyterLab plugins. Instead it exports an interface given in the
  536. `rendermime-interfaces <https://jupyterlab.github.io/jupyterlab/interfaces/_rendermime_interfaces_src_index_.irendermime.iextension.html>`__
  537. package.
  538. The JupyterLab repo has an example mime renderer extension for
  539. `pdf <https://github.com/jupyterlab/jupyterlab/tree/master/packages/pdf-extension>`__
  540. files. It provides a mime renderer for pdf data and registers itself as
  541. a document renderer for pdf file types.
  542. The JupyterLab organization also has a mime renderer extension tutorial
  543. which adds mp4 video rendering to the application
  544. `here <https://github.com/jupyterlab/jupyterlab-mp4>`__.
  545. The ``rendermime-interfaces`` package is intended to be the only
  546. JupyterLab package needed to create a mime renderer extension (using the
  547. interfaces in TypeScript or as a form of documentation if using plain
  548. JavaScript).
  549. The only other difference from a standard extension is that has a
  550. ``jupyterlab`` key in its ``package.json`` with ``"mimeExtension"``
  551. metadata. The value can be ``true`` to use the main module of the
  552. package, or a string path to a specific module (e.g. ``"lib/foo"``).
  553. The mime renderer can update its data by calling ``.setData()`` on the
  554. model it is given to render. This can be used for example to add a
  555. ``png`` representation of a dynamic figure, which will be picked up by a
  556. notebook model and added to the notebook document. When using
  557. ``IDocumentWidgetFactoryOptions``, you can update the document model by
  558. calling ``.setData()`` with updated data for the rendered MIME type. The
  559. document can then be saved by the user in the usual manner.
  560. Themes
  561. ------
  562. A theme is a JupyterLab extension that uses a ``ThemeManager`` and can
  563. be loaded and unloaded dynamically. The package must include all static
  564. assets that are referenced by ``url()`` in its CSS files. Local URLs can
  565. be used to reference files relative to the location of the referring sibling CSS files. For example ``url('images/foo.png')`` or
  566. ``url('../foo/bar.css')``\ can be used to refer local files in the
  567. theme. Absolute URLs (starting with a ``/``) or external URLs (e.g.
  568. ``https:``) can be used to refer to external assets. The path to the
  569. theme asset entry point is specified ``package.json`` under the ``"jupyterlab"``
  570. key as ``"themePath"``. See the `JupyterLab Light
  571. Theme <https://github.com/jupyterlab/jupyterlab/tree/master/packages/theme-light-extension>`__
  572. for an example. Ensure that the theme files are included in the
  573. ``"files"`` metadata in ``package.json``. Note that if you want to use SCSS, SASS, or LESS files,
  574. you must compile them to CSS and point JupyterLab to the CSS files.
  575. The theme extension is installed in the same way as a regular extension (see
  576. `extension authoring <#extension-authoring>`__).
  577. It is also possible to create a new theme using the
  578. `TypeScript theme cookiecutter <https://github.com/jupyterlab/theme-cookiecutter>`__.
  579. Standard (General-Purpose) Extensions
  580. -------------------------------------
  581. JupyterLab's modular architecture is based around the idea
  582. that all extensions are on equal footing, and that they interact
  583. with each other through typed interfaces that are provided by ``Token`` objects.
  584. An extension can provide a ``Token`` to the application,
  585. which other extensions can then request for their own use.
  586. Standard Extension Example
  587. ^^^^^^^^^^^^^^^^^^^^^^^^^^
  588. For a concrete example of a standard extension, see :ref:`How to extend the Notebook plugin <extend-notebook-plugin>`.
  589. Notice that the mime renderer extensions use a limited,
  590. simplified interface to JupyterLab's extension system. Modifying the
  591. notebook plugin requires the full, general-purpose interface to the
  592. extension system.
  593. Storing Extension Data
  594. ^^^^^^^^^^^^^^^^^^^^^^
  595. In addition to the file system that is accessed by using the
  596. ``@jupyterlab/services`` package, JupyterLab exposes a plugin settings
  597. system that can be used to provide default setting values and user overrides.
  598. Extension Settings
  599. """"""""""""""""""
  600. An extension can specify user settings using a JSON Schema. The schema
  601. definition should be in a file that resides in the ``schemaDir``
  602. directory that is specified in the ``package.json`` file of the
  603. extension. The actual file name should use is the part that follows the
  604. package name of extension. So for example, the JupyterLab
  605. ``apputils-extension`` package hosts several plugins:
  606. - ``'@jupyterlab/apputils-extension:menu'``
  607. - ``'@jupyterlab/apputils-extension:palette'``
  608. - ``'@jupyterlab/apputils-extension:settings'``
  609. - ``'@jupyterlab/apputils-extension:themes'``
  610. And in the ``package.json`` for ``@jupyterlab/apputils-extension``, the
  611. ``schemaDir`` field is a directory called ``schema``. Since the
  612. ``themes`` plugin requires a JSON schema, its schema file location is:
  613. ``schema/themes.json``. The plugin's name is used to automatically
  614. associate it with its settings file, so this naming convention is
  615. important. Ensure that the schema files are included in the ``"files"``
  616. metadata in ``package.json``.
  617. See the
  618. `fileeditor-extension <https://github.com/jupyterlab/jupyterlab/tree/master/packages/fileeditor-extension>`__
  619. for another example of an extension that uses settings.
  620. Note: You can override default values of the extension settings by
  621. defining new default values in an ``overrides.json`` file in the
  622. application settings directory. So for example, if you would like
  623. to set the dark theme by default instead of the light one, an
  624. ``overrides.json`` file containing the following lines needs to be
  625. added in the application settings directory (by default this is the
  626. ``share/jupyter/lab/settings`` folder).
  627. .. code:: json
  628. {
  629. "@jupyterlab/apputils-extension:themes": {
  630. "theme": "JupyterLab Dark"
  631. }
  632. }
  633. State Database
  634. """"""""""""""
  635. The state database can be accessed by importing ``IStateDB`` from
  636. ``@jupyterlab/statedb`` and adding it to the list of ``requires`` for
  637. a plugin:
  638. .. code:: typescript
  639. const id = 'foo-extension:IFoo';
  640. const IFoo = new Token<IFoo>(id);
  641. interface IFoo {}
  642. class Foo implements IFoo {}
  643. const plugin: JupyterFrontEndPlugin<IFoo> = {
  644. id,
  645. requires: [IStateDB],
  646. provides: IFoo,
  647. activate: (app: JupyterFrontEnd, state: IStateDB): IFoo => {
  648. const foo = new Foo();
  649. const key = `${id}:some-attribute`;
  650. // Load the saved plugin state and apply it once the app
  651. // has finished restoring its former layout.
  652. Promise.all([state.fetch(key), app.restored])
  653. .then(([saved]) => { /* Update `foo` with `saved`. */ });
  654. // Fulfill the plugin contract by returning an `IFoo`.
  655. return foo;
  656. },
  657. autoStart: true
  658. };
  659. Context Menus
  660. ^^^^^^^^^^^^^
  661. JupyterLab has an application-wide context menu available as
  662. ``app.contextMenu``. See the Lumino
  663. `docs <https://jupyterlab.github.io/lumino/widgets/interfaces/contextmenu.iitemoptions.html>`__
  664. for the item creation options. If you wish to preempt the
  665. application context menu, you can use a 'contextmenu' event listener and
  666. call ``event.stopPropagation`` to prevent the application context menu
  667. handler from being called (it is listening in the bubble phase on the
  668. ``document``). At this point you could show your own Lumino
  669. `contextMenu <https://jupyterlab.github.io/lumino/widgets/classes/contextmenu.html>`__,
  670. or simply stop propagation and let the system context menu be shown.
  671. This would look something like the following in a ``Widget`` subclass:
  672. .. code:: javascript
  673. // In `onAfterAttach()`
  674. this.node.addEventListener('contextmenu', this);
  675. // In `handleEvent()`
  676. case 'contextmenu':
  677. event.stopPropagation();
  678. .. |dependencies| image:: dependency-graph.svg
  679. Using React
  680. ^^^^^^^^^^^
  681. We also provide support for using :ref:`react` in your JupyterLab
  682. extensions, as well as in the core codebase.
  683. .. _ext-author-companion-packages:
  684. Companion Packages
  685. ^^^^^^^^^^^^^^^^^^
  686. If your extensions depends on the presence of one or more packages in the
  687. kernel, or on a notebook server extension, you can add metadata to indicate
  688. this to the extension manager by adding metadata to your package.json file.
  689. The full options available are::
  690. "jupyterlab": {
  691. "discovery": {
  692. "kernel": [
  693. {
  694. "kernel_spec": {
  695. "language": "<regexp for matching kernel language>",
  696. "display_name": "<regexp for matching kernel display name>" // optional
  697. },
  698. "base": {
  699. "name": "<the name of the kernel package>"
  700. },
  701. "overrides": { // optional
  702. "<manager name, e.g. 'pip'>": {
  703. "name": "<name of kernel package on pip, if it differs from base name>"
  704. }
  705. },
  706. "managers": [ // list of package managers that have your kernel package
  707. "pip",
  708. "conda"
  709. ]
  710. }
  711. ],
  712. "server": {
  713. "base": {
  714. "name": "<the name of the server extension package>"
  715. },
  716. "overrides": { // optional
  717. "<manager name, e.g. 'pip'>": {
  718. "name": "<name of server extension package on pip, if it differs from base name>"
  719. }
  720. },
  721. "managers": [ // list of package managers that have your server extension package
  722. "pip",
  723. "conda"
  724. ]
  725. }
  726. }
  727. }
  728. A typical setup for e.g. a jupyter-widget based package will then be::
  729. "keywords": [
  730. "jupyterlab-extension",
  731. "jupyter",
  732. "widgets",
  733. "jupyterlab"
  734. ],
  735. "jupyterlab": {
  736. "extension": true,
  737. "discovery": {
  738. "kernel": [
  739. {
  740. "kernel_spec": {
  741. "language": "^python",
  742. },
  743. "base": {
  744. "name": "myipywidgetspackage"
  745. },
  746. "managers": [
  747. "pip",
  748. "conda"
  749. ]
  750. }
  751. ]
  752. }
  753. }
  754. Currently supported package managers are:
  755. - ``pip``
  756. - ``conda``
  757. Shipping Packages
  758. ^^^^^^^^^^^^^^^^^
  759. Most extensions are single JavaScript packages, and can be shipped on npmjs.org.
  760. This makes them discoverable by the JupyterLab extension manager, provided they
  761. have the ``jupyterlab-extension`` keyword in their ``package.json``. If the package also
  762. contains a server extension (Python package), the author has two options.
  763. The server extension and the JupyterLab extension can be shipped in a single package,
  764. or they can be shipped separately.
  765. The JupyterLab extension can be bundled in a package on PyPI and conda-forge so
  766. that it ends up in the user's application directory. Note that the user will still have to run ``jupyter lab build``
  767. (or build when prompted in the UI) in order to use the extension.
  768. The general idea is to pack the Jupyterlab extension using ``npm pack``, and then
  769. use the ``data_files`` logic in ``setup.py`` to ensure the file ends up in the
  770. ``<jupyterlab_application>/share/jupyter/lab/extensions``
  771. directory.
  772. Note that even if the JupyterLab extension is unusable without the
  773. server extension, as long as you use the companion package metadata it is still
  774. useful to publish it to npmjs.org so it is discoverable by the JupyterLab extension manager.
  775. The server extension can be enabled on install by using ``data_files``.
  776. an example of this approach is `jupyterlab-matplotlib <https://github.com/matplotlib/jupyter-matplotlib/tree/ce9cc91e52065d33e57c3265282640f2aa44e08f>`__. The file used to enable the server extension is `here <https://github.com/matplotlib/jupyter-matplotlib/blob/ce9cc91e52065d33e57c3265282640f2aa44e08f/jupyter-matplotlib.json>`__. The logic to ship the JS tarball and server extension
  777. enabler is in `setup.py <https://github.com/matplotlib/jupyter-matplotlib/blob/ce9cc91e52065d33e57c3265282640f2aa44e08f/setup.py>`__. Note that the ``setup.py``
  778. file has additional logic to automatically create the JS tarball as part of the
  779. release process, but this could also be done manually.
  780. Technically, a package that contains only a JupyterLab extension could be created
  781. and published on ``conda-forge``, but it would not be discoverable by the JupyterLab
  782. extension manager.