From a16b144805462c7d3f8ed62c23b1c0690e96feb8 Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Tue, 3 Sep 2019 19:05:45 +0100 Subject: [PATCH 01/20] Block fetching the settings for a plugin that is disabled. --- packages/services/src/setting/index.ts | 38 ++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/packages/services/src/setting/index.ts b/packages/services/src/setting/index.ts index b1aecf6c6c9d..b119d09d3e94 100644 --- a/packages/services/src/setting/index.ts +++ b/packages/services/src/setting/index.ts @@ -1,7 +1,12 @@ // Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. -import { DataConnector, ISettingRegistry, URLExt } from '@jupyterlab/coreutils'; +import { + DataConnector, + ISettingRegistry, + PageConfig, + URLExt +} from '@jupyterlab/coreutils'; import { ServerConnection } from '../serverconnection'; @@ -50,6 +55,10 @@ export class SettingManager extends DataConnector< throw new Error('Plugin `id` parameter is required for settings fetch.'); } + if (Private.isDisabled(id)) { + throw new Error(`Plugin ${id} is disabled.`); + } + if (response.status !== 200) { throw new ResponseError(response); } @@ -76,12 +85,14 @@ export class SettingManager extends DataConnector< } const json = await response.json(); - const values = ((json || {})['settings'] || []).map( + const values = (((json || {})['settings'] || []).map( (plugin: ISettingRegistry.IPlugin) => { plugin.data = { composite: {}, user: {} }; return plugin; } - ) as ISettingRegistry.IPlugin[]; + ) as ISettingRegistry.IPlugin[]).filter( + plugin => !Private.isDisabled(plugin.id) + ); const ids = values.map(plugin => plugin.id); return { ids, values }; @@ -140,6 +151,27 @@ export namespace Setting { * A namespace for private data. */ namespace Private { + let disabled: { raw: string; rule: RegExp }[] = []; + try { + let tempDisabled = PageConfig.getOption('disabledExtensions'); + if (tempDisabled) { + disabled = JSON.parse(tempDisabled).map((pattern: string) => { + return { raw: pattern, rule: new RegExp(pattern) }; + }); + } + } catch (error) { + console.warn('Unable to parse disabled extensions.', error); + } + + /** + * Return `true` if a plugin has been disabled. + */ + export function isDisabled(plugin: string): boolean { + return disabled.some( + pattern => pattern.raw === plugin || pattern.rule.test(plugin) + ); + } + /** * Get the url for a plugin's settings. */ From e93fe11742eb241869f8039c71e46e92e6018f4b Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Wed, 4 Sep 2019 11:05:16 +0100 Subject: [PATCH 02/20] Address @vidartf's comments. --- packages/services/src/setting/index.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/services/src/setting/index.ts b/packages/services/src/setting/index.ts index b119d09d3e94..cc536ae9fa52 100644 --- a/packages/services/src/setting/index.ts +++ b/packages/services/src/setting/index.ts @@ -44,13 +44,6 @@ export class SettingManager extends DataConnector< * @returns A promise that resolves if successful. */ async fetch(id: string): Promise { - const { serverSettings } = this; - const { baseUrl, appUrl } = serverSettings; - const { makeRequest, ResponseError } = ServerConnection; - const base = baseUrl + appUrl; - const url = Private.url(base, id); - const response = await makeRequest(url, {}, serverSettings); - if (!id) { throw new Error('Plugin `id` parameter is required for settings fetch.'); } @@ -59,6 +52,13 @@ export class SettingManager extends DataConnector< throw new Error(`Plugin ${id} is disabled.`); } + const { serverSettings } = this; + const { baseUrl, appUrl } = serverSettings; + const { makeRequest, ResponseError } = ServerConnection; + const base = baseUrl + appUrl; + const url = Private.url(base, id); + const response = await makeRequest(url, {}, serverSettings); + if (response.status !== 200) { throw new ResponseError(response); } From 60a0951e3ee37c9f846965d6da6f776152713bd0 Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Thu, 5 Sep 2019 15:36:14 +0100 Subject: [PATCH 03/20] Remove deprecated shortcuts plugin. --- .../shortcuts-extension/schema/plugin.json | 24 ----- packages/shortcuts-extension/src/index.ts | 97 +------------------ 2 files changed, 2 insertions(+), 119 deletions(-) delete mode 100644 packages/shortcuts-extension/schema/plugin.json diff --git a/packages/shortcuts-extension/schema/plugin.json b/packages/shortcuts-extension/schema/plugin.json deleted file mode 100644 index 15eb4eb2d6fc..000000000000 --- a/packages/shortcuts-extension/schema/plugin.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "jupyter.lab.setting-deprecated": true, - "jupyter.lab.setting-icon-class": "jp-KeyboardIcon", - "jupyter.lab.setting-icon-label": "Keyboard Shortcuts", - "title": "Keyboard Shortcuts", - "description": "Keyboard shortcut settings for JupyterLab.", - "properties": {}, - "oneOf": [{ "$ref": "#/definitions/shortcut" }], - "type": "object", - "definitions": { - "shortcut": { - "properties": { - "command": { "type": "string" }, - "keys": { - "items": { "type": "string" }, - "minItems": 1, - "type": "array" - }, - "selector": { "type": "string" } - }, - "type": "object" - } - } -} diff --git a/packages/shortcuts-extension/src/index.ts b/packages/shortcuts-extension/src/index.ts index 4d36730ae032..f2fc3188b464 100644 --- a/packages/shortcuts-extension/src/index.ts +++ b/packages/shortcuts-extension/src/index.ts @@ -18,97 +18,6 @@ import { import { DisposableSet, IDisposable } from '@phosphor/disposable'; -/** - * The ASCII record separator character. - */ -const RECORD_SEPARATOR = String.fromCharCode(30); - -/** - * This plugin and its schema are deprecated and will be removed in a future - * version of JupyterLab. This plugin will load old keyboard shortcuts and add - * them to the new keyboard shortcuts plugin below before removing the old - * shortcuts. - */ -const plugin: JupyterFrontEndPlugin = { - id: '@jupyterlab/shortcuts-extension:plugin', - requires: [ISettingRegistry], - activate: async (app: JupyterFrontEnd, registry: ISettingRegistry) => { - try { - const old = await registry.load(plugin.id); - const settings = await registry.load(shortcuts.id); - const keys = Object.keys(old.user); - const deprecated: ISettingRegistry.IShortcut[] = []; - const port = (deprecated: ISettingRegistry.IShortcut[]) => { - if (!deprecated.length) { - return; - } - - const memo: { - [keys: string]: { [selector: string]: null }; - } = {}; - const shortcuts = settings.user - .shortcuts as ISettingRegistry.IShortcut[]; - - // Add the current shortcuts into the memo. - shortcuts.forEach(shortcut => { - const keys = shortcut.keys.join(RECORD_SEPARATOR); - const { selector } = shortcut; - - if (!keys) { - return; - } - if (!(keys in memo)) { - memo[keys] = {}; - } - if (!(selector in memo[keys])) { - memo[keys][selector] = null; - } - }); - - // Add deprecated shortcuts that don't exist to the current list. - deprecated.forEach(shortcut => { - const { selector } = shortcut; - const keys = shortcut.keys.join(RECORD_SEPARATOR); - - if (!(keys in memo)) { - memo[keys] = {}; - } - if (!(selector in memo[keys])) { - memo[keys][selector] = null; - shortcuts.push(shortcut); - } - }); - - // Save the reconciled list. - void settings.set('shortcuts', shortcuts); - }; - - if (!keys.length) { - return; - } - keys.forEach(key => { - const { command, keys, selector } = old.user[ - key - ] as ISettingRegistry.IShortcut; - - // Only port shortcuts over if they are valid. - if (command && selector && keys && keys.length) { - deprecated.push({ command, keys, selector }); - } - }); - - // Port the deprecated shortcuts to the new plugin. - port(deprecated); - - // Remove all old shortcuts; - void old.save('{}'); - } catch (error) { - console.error(`Loading ${plugin.id} failed.`, error); - } - }, - autoStart: true -}; - /** * The default shortcuts extension. * @@ -259,11 +168,9 @@ List of Keyboard Shortcuts`; }; /** - * Export the plugins as default. + * Export the shortcut plugin as default. */ -const plugins: JupyterFrontEndPlugin[] = [plugin, shortcuts]; - -export default plugins; +export default shortcuts; /** * A namespace for private module data. From 9790572f375d04f262c7b687f1f61906e36518c4 Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Fri, 6 Sep 2019 12:49:41 +0100 Subject: [PATCH 04/20] Add Extension namespace and functions to PageConfig. --- packages/coreutils/src/pageconfig.ts | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/packages/coreutils/src/pageconfig.ts b/packages/coreutils/src/pageconfig.ts index 3d85754de1a0..b682a5540fb1 100644 --- a/packages/coreutils/src/pageconfig.ts +++ b/packages/coreutils/src/pageconfig.ts @@ -196,14 +196,6 @@ export namespace PageConfig { * The namespace for page config `Extension` functions. */ export namespace Extension { - /** - * Populate an array from page config. - * - * @param key - The page config key (e.g., `deferredExtensions`). - * - * #### Notes - * This is intended for `deferredExtensions` and `disabledExtensions`. - */ function populate(key: string): { raw: string; rule: RegExp }[] { try { const raw = getOption(key); @@ -218,30 +210,14 @@ export namespace PageConfig { return []; } - /** - * The collection of deferred extensions in page config. - */ export const deferred = populate('deferredExtensions'); - /** - * The collection of disabled extensions in page config. - */ export const disabled = populate('disabledExtensions'); - /** - * Returns whether a plugin is deferred. - * - * @param id - The plugin ID. - */ export function isDeferred(id: string): boolean { return deferred.some(val => val.raw === id || val.rule.test(id)); } - /** - * Returns whether a plugin is disabled. - * - * @param id - The plugin ID. - */ export function isDisabled(id: string): boolean { return disabled.some(val => val.raw === id || val.rule.test(id)); } From 998330cf48d39c233e9457f2766f46502c4f306f Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Fri, 6 Sep 2019 13:29:12 +0100 Subject: [PATCH 05/20] Remove filtering from settings service. --- packages/services/src/setting/index.ts | 38 ++------------------------ 1 file changed, 3 insertions(+), 35 deletions(-) diff --git a/packages/services/src/setting/index.ts b/packages/services/src/setting/index.ts index cc536ae9fa52..fac9bb060a53 100644 --- a/packages/services/src/setting/index.ts +++ b/packages/services/src/setting/index.ts @@ -1,12 +1,7 @@ // Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. -import { - DataConnector, - ISettingRegistry, - PageConfig, - URLExt -} from '@jupyterlab/coreutils'; +import { DataConnector, ISettingRegistry, URLExt } from '@jupyterlab/coreutils'; import { ServerConnection } from '../serverconnection'; @@ -48,10 +43,6 @@ export class SettingManager extends DataConnector< throw new Error('Plugin `id` parameter is required for settings fetch.'); } - if (Private.isDisabled(id)) { - throw new Error(`Plugin ${id} is disabled.`); - } - const { serverSettings } = this; const { baseUrl, appUrl } = serverSettings; const { makeRequest, ResponseError } = ServerConnection; @@ -85,14 +76,12 @@ export class SettingManager extends DataConnector< } const json = await response.json(); - const values = (((json || {})['settings'] || []).map( + const values = ((json || {})['settings'] || []).map( (plugin: ISettingRegistry.IPlugin) => { plugin.data = { composite: {}, user: {} }; return plugin; } - ) as ISettingRegistry.IPlugin[]).filter( - plugin => !Private.isDisabled(plugin.id) - ); + ) as ISettingRegistry.IPlugin[]; const ids = values.map(plugin => plugin.id); return { ids, values }; @@ -151,27 +140,6 @@ export namespace Setting { * A namespace for private data. */ namespace Private { - let disabled: { raw: string; rule: RegExp }[] = []; - try { - let tempDisabled = PageConfig.getOption('disabledExtensions'); - if (tempDisabled) { - disabled = JSON.parse(tempDisabled).map((pattern: string) => { - return { raw: pattern, rule: new RegExp(pattern) }; - }); - } - } catch (error) { - console.warn('Unable to parse disabled extensions.', error); - } - - /** - * Return `true` if a plugin has been disabled. - */ - export function isDisabled(plugin: string): boolean { - return disabled.some( - pattern => pattern.raw === plugin || pattern.rule.test(plugin) - ); - } - /** * Get the url for a plugin's settings. */ From fe022a65ac638de8ee9ce188c9bc47554d494729 Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Fri, 6 Sep 2019 14:32:48 +0100 Subject: [PATCH 06/20] Handle setting plugin filtering at the setting registry plugin level instead of filtering via the service manager. --- packages/apputils-extension/src/index.ts | 25 +++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/apputils-extension/src/index.ts b/packages/apputils-extension/src/index.ts index 415803d8b710..03b74dd586fc 100644 --- a/packages/apputils-extension/src/index.ts +++ b/packages/apputils-extension/src/index.ts @@ -20,10 +20,12 @@ import { } from '@jupyterlab/apputils'; import { + DataConnector, Debouncer, IRateLimiter, ISettingRegistry, IStateDB, + PageConfig, SettingRegistry, StateDB, Throttler, @@ -90,7 +92,28 @@ const paletteRestorer: JupyterFrontEndPlugin = { const settings: JupyterFrontEndPlugin = { id: '@jupyterlab/apputils-extension:settings', activate: async (app: JupyterFrontEnd): Promise => { - const connector = app.serviceManager.settings; + const connector = new (class SettingConnector extends DataConnector< + ISettingRegistry.IPlugin, + string + > { + fetch(id: string): Promise { + return app.serviceManager.settings.fetch(id); + } + + async list(): Promise<{ + ids: string[]; + values: ISettingRegistry.IPlugin[]; + }> { + const { ids, values } = await app.serviceManager.settings.list(); + const filtered = { + ids: ids.filter(id => !PageConfig.Extension.isDisabled(id)), + values: values.filter( + ({ id }) => !PageConfig.Extension.isDisabled(id) + ) + }; + return filtered; + } + })(); const plugins = (await connector.list()).values; return new SettingRegistry({ connector, plugins }); From 0682f78661d3a4f6cb1db5294f72ceff074a98e9 Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Fri, 6 Sep 2019 16:15:37 +0100 Subject: [PATCH 07/20] Update dev_mode index file to use new PageConfig functions and fix disabled bug for individual plugins. --- dev_mode/index.js | 128 ++++++++++++++++------------------------------ 1 file changed, 44 insertions(+), 84 deletions(-) diff --git a/dev_mode/index.js b/dev_mode/index.js index abc0058763cf..56292397aa15 100644 --- a/dev_mode/index.js +++ b/dev_mode/index.js @@ -21,50 +21,9 @@ require('./imports.css'); */ function main() { var JupyterLab = require('@jupyterlab/application').JupyterLab; - - // Get the disabled extensions. - var disabled = { patterns: [], matches: [] }; - var disabledExtensions = []; - try { - var tempDisabled = PageConfig.getOption('disabledExtensions'); - if (tempDisabled) { - disabledExtensions = JSON.parse(tempDisabled).map(function(pattern) { - disabled.patterns.push(pattern); - return { raw: pattern, rule: new RegExp(pattern) }; - }); - } - } catch (error) { - console.warn('Unable to parse disabled extensions.', error); - } - - // Get the deferred extensions. - var deferred = { patterns: [], matches: [] }; - var deferredExtensions = []; + var disabled = []; + var deferred = []; var ignorePlugins = []; - try { - var tempDeferred = PageConfig.getOption('deferredExtensions'); - if (tempDeferred) { - deferredExtensions = JSON.parse(tempDeferred).map(function(pattern) { - deferred.patterns.push(pattern); - return { raw: pattern, rule: new RegExp(pattern) }; - }); - } - } catch (error) { - console.warn('Unable to parse deferred extensions.', error); - } - - function isDeferred(value) { - return deferredExtensions.some(function(pattern) { - return pattern.raw === value || pattern.rule.test(value); - }); - } - - function isDisabled(value) { - return disabledExtensions.some(function(pattern) { - return pattern.raw === value || pattern.rule.test(value); - }); - } - var register = []; // Handle the registered mime extensions. @@ -73,12 +32,12 @@ function main() { var extMod; {{#each jupyterlab_mime_extensions}} try { - if (isDeferred('{{key}}')) { - deferred.matches.push('{{key}}'); + if (PageConfig.Extension.isDeferred('{{key}}')) { + deferred.push('{{key}}'); ignorePlugins.push('{{key}}'); } - if (isDisabled('{{@key}}')) { - disabled.matches.push('{{@key}}'); + if (PageConfig.Extension.isDisabled('{{@key}}')) { + disabled.push('{{@key}}'); } else { extMod = require('{{@key}}/{{this}}'); extension = extMod.default; @@ -88,21 +47,18 @@ function main() { extension = extMod; } - if (Array.isArray(extension)) { - extension.forEach(function(plugin) { - if (isDeferred(plugin.id)) { - deferred.matches.push(plugin.id); - ignorePlugins.push(plugin.id); - } - if (isDisabled(plugin.id)) { - disabled.matches.push(plugin.id); - return; - } - mimeExtensions.push(plugin); - }); - } else { - mimeExtensions.push(extension); - } + var list = Array.isArray(extension) ? extension : [extension]; + list.forEach(function(plugin) { + if (PageConfig.Extension.isDeferred(plugin.id)) { + deferred.push(plugin.id); + ignorePlugins.push(plugin.id); + } + if (PageConfig.Extension.isDisabled(plugin.id)) { + disabled.push(plugin.id); + return; + } + mimeExtensions.push(plugin); + }); } } catch (e) { console.error(e); @@ -112,12 +68,12 @@ function main() { // Handled the registered standard extensions. {{#each jupyterlab_extensions}} try { - if (isDeferred('{{key}}')) { - deferred.matches.push('{{key}}'); + if (PageConfig.Extension.isDeferred('{{key}}')) { + deferred.push('{{key}}'); ignorePlugins.push('{{key}}'); } - if (isDisabled('{{@key}}')) { - disabled.matches.push('{{@key}}'); + if (PageConfig.Extension.isDisabled('{{@key}}')) { + disabled.push('{{@key}}'); } else { extMod = require('{{@key}}/{{this}}'); extension = extMod.default; @@ -127,31 +83,35 @@ function main() { extension = extMod; } - if (Array.isArray(extension)) { - extension.forEach(function(plugin) { - if (isDeferred(plugin.id)) { - deferred.matches.push(plugin.id); - ignorePlugins.push(plugin.id); - } - if (isDisabled(plugin.id)) { - disabled.matches.push(plugin.id); - return; - } - register.push(plugin); - }); - } else { - register.push(extension); - } + var list = Array.isArray(extension) ? extension : [extension]; + list.forEach(function(plugin) { + if (PageConfig.Extension.isDeferred(plugin.id)) { + deferred.push(plugin.id); + ignorePlugins.push(plugin.id); + } + if (PageConfig.Extension.isDisabled(plugin.id)) { + disabled.push(plugin.id); + return; + } + register.push(plugin); + }); } } catch (e) { console.error(e); } {{/each}} - var lab = new JupyterLab({ mimeExtensions: mimeExtensions, - disabled: disabled, - deferred: deferred + disabled: { + matches: disabled, + patterns: PageConfig.Extension.disabled + .map(function (val) { return val.raw; }) + }, + deferred: { + matches: deferred, + patterns: PageConfig.Extension.deferred + .map(function (val) { return val.raw; }) + }, }); register.forEach(function(item) { lab.registerPluginModule(item); }); lab.start({ ignorePlugins: ignorePlugins }); From fa131df8155ef7084448ce44f46ae5b57a6bf532 Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Fri, 6 Sep 2019 16:15:55 +0100 Subject: [PATCH 08/20] Remove superfluous type annotation. --- packages/apputils-extension/src/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/apputils-extension/src/index.ts b/packages/apputils-extension/src/index.ts index 03b74dd586fc..89d365a71378 100644 --- a/packages/apputils-extension/src/index.ts +++ b/packages/apputils-extension/src/index.ts @@ -22,7 +22,6 @@ import { import { DataConnector, Debouncer, - IRateLimiter, ISettingRegistry, IStateDB, PageConfig, @@ -213,7 +212,7 @@ const splash: JupyterFrontEndPlugin = { // Create debounced recovery dialog function. let dialog: Dialog; - const recovery: IRateLimiter = new Throttler(async () => { + const recovery = new Throttler(async () => { if (dialog) { return; } From 50bd8121ae53ce127daecd8b0890538188b7e5d4 Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Fri, 6 Sep 2019 16:17:49 +0100 Subject: [PATCH 09/20] Update staging index. --- jupyterlab/staging/index.js | 128 +++++++++++++----------------------- 1 file changed, 44 insertions(+), 84 deletions(-) diff --git a/jupyterlab/staging/index.js b/jupyterlab/staging/index.js index abc0058763cf..56292397aa15 100644 --- a/jupyterlab/staging/index.js +++ b/jupyterlab/staging/index.js @@ -21,50 +21,9 @@ require('./imports.css'); */ function main() { var JupyterLab = require('@jupyterlab/application').JupyterLab; - - // Get the disabled extensions. - var disabled = { patterns: [], matches: [] }; - var disabledExtensions = []; - try { - var tempDisabled = PageConfig.getOption('disabledExtensions'); - if (tempDisabled) { - disabledExtensions = JSON.parse(tempDisabled).map(function(pattern) { - disabled.patterns.push(pattern); - return { raw: pattern, rule: new RegExp(pattern) }; - }); - } - } catch (error) { - console.warn('Unable to parse disabled extensions.', error); - } - - // Get the deferred extensions. - var deferred = { patterns: [], matches: [] }; - var deferredExtensions = []; + var disabled = []; + var deferred = []; var ignorePlugins = []; - try { - var tempDeferred = PageConfig.getOption('deferredExtensions'); - if (tempDeferred) { - deferredExtensions = JSON.parse(tempDeferred).map(function(pattern) { - deferred.patterns.push(pattern); - return { raw: pattern, rule: new RegExp(pattern) }; - }); - } - } catch (error) { - console.warn('Unable to parse deferred extensions.', error); - } - - function isDeferred(value) { - return deferredExtensions.some(function(pattern) { - return pattern.raw === value || pattern.rule.test(value); - }); - } - - function isDisabled(value) { - return disabledExtensions.some(function(pattern) { - return pattern.raw === value || pattern.rule.test(value); - }); - } - var register = []; // Handle the registered mime extensions. @@ -73,12 +32,12 @@ function main() { var extMod; {{#each jupyterlab_mime_extensions}} try { - if (isDeferred('{{key}}')) { - deferred.matches.push('{{key}}'); + if (PageConfig.Extension.isDeferred('{{key}}')) { + deferred.push('{{key}}'); ignorePlugins.push('{{key}}'); } - if (isDisabled('{{@key}}')) { - disabled.matches.push('{{@key}}'); + if (PageConfig.Extension.isDisabled('{{@key}}')) { + disabled.push('{{@key}}'); } else { extMod = require('{{@key}}/{{this}}'); extension = extMod.default; @@ -88,21 +47,18 @@ function main() { extension = extMod; } - if (Array.isArray(extension)) { - extension.forEach(function(plugin) { - if (isDeferred(plugin.id)) { - deferred.matches.push(plugin.id); - ignorePlugins.push(plugin.id); - } - if (isDisabled(plugin.id)) { - disabled.matches.push(plugin.id); - return; - } - mimeExtensions.push(plugin); - }); - } else { - mimeExtensions.push(extension); - } + var list = Array.isArray(extension) ? extension : [extension]; + list.forEach(function(plugin) { + if (PageConfig.Extension.isDeferred(plugin.id)) { + deferred.push(plugin.id); + ignorePlugins.push(plugin.id); + } + if (PageConfig.Extension.isDisabled(plugin.id)) { + disabled.push(plugin.id); + return; + } + mimeExtensions.push(plugin); + }); } } catch (e) { console.error(e); @@ -112,12 +68,12 @@ function main() { // Handled the registered standard extensions. {{#each jupyterlab_extensions}} try { - if (isDeferred('{{key}}')) { - deferred.matches.push('{{key}}'); + if (PageConfig.Extension.isDeferred('{{key}}')) { + deferred.push('{{key}}'); ignorePlugins.push('{{key}}'); } - if (isDisabled('{{@key}}')) { - disabled.matches.push('{{@key}}'); + if (PageConfig.Extension.isDisabled('{{@key}}')) { + disabled.push('{{@key}}'); } else { extMod = require('{{@key}}/{{this}}'); extension = extMod.default; @@ -127,31 +83,35 @@ function main() { extension = extMod; } - if (Array.isArray(extension)) { - extension.forEach(function(plugin) { - if (isDeferred(plugin.id)) { - deferred.matches.push(plugin.id); - ignorePlugins.push(plugin.id); - } - if (isDisabled(plugin.id)) { - disabled.matches.push(plugin.id); - return; - } - register.push(plugin); - }); - } else { - register.push(extension); - } + var list = Array.isArray(extension) ? extension : [extension]; + list.forEach(function(plugin) { + if (PageConfig.Extension.isDeferred(plugin.id)) { + deferred.push(plugin.id); + ignorePlugins.push(plugin.id); + } + if (PageConfig.Extension.isDisabled(plugin.id)) { + disabled.push(plugin.id); + return; + } + register.push(plugin); + }); } } catch (e) { console.error(e); } {{/each}} - var lab = new JupyterLab({ mimeExtensions: mimeExtensions, - disabled: disabled, - deferred: deferred + disabled: { + matches: disabled, + patterns: PageConfig.Extension.disabled + .map(function (val) { return val.raw; }) + }, + deferred: { + matches: deferred, + patterns: PageConfig.Extension.deferred + .map(function (val) { return val.raw; }) + }, }); register.forEach(function(item) { lab.registerPluginModule(item); }); lab.start({ ignorePlugins: ignorePlugins }); From b2ad4a7e91cfe1008af87e7a316fe502fd3bda23 Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Fri, 6 Sep 2019 16:33:41 +0100 Subject: [PATCH 10/20] Clean up disabled/deferred handling. --- dev_mode/index.js | 89 +++++++++++++++---------------------- jupyterlab/staging/index.js | 89 +++++++++++++++---------------------- 2 files changed, 74 insertions(+), 104 deletions(-) diff --git a/dev_mode/index.js b/dev_mode/index.js index 56292397aa15..17e12088e781 100644 --- a/dev_mode/index.js +++ b/dev_mode/index.js @@ -30,36 +30,29 @@ function main() { var mimeExtensions = []; var extension; var extMod; + var plugins = []; {{#each jupyterlab_mime_extensions}} try { - if (PageConfig.Extension.isDeferred('{{key}}')) { - deferred.push('{{key}}'); - ignorePlugins.push('{{key}}'); - } - if (PageConfig.Extension.isDisabled('{{@key}}')) { - disabled.push('{{@key}}'); - } else { - extMod = require('{{@key}}/{{this}}'); - extension = extMod.default; - - // Handle CommonJS exports. - if (!extMod.hasOwnProperty('__esModule')) { - extension = extMod; - } + extMod = require('{{@key}}/{{this}}'); + extension = extMod.default; - var list = Array.isArray(extension) ? extension : [extension]; - list.forEach(function(plugin) { - if (PageConfig.Extension.isDeferred(plugin.id)) { - deferred.push(plugin.id); - ignorePlugins.push(plugin.id); - } - if (PageConfig.Extension.isDisabled(plugin.id)) { - disabled.push(plugin.id); - return; - } - mimeExtensions.push(plugin); - }); + // Handle CommonJS exports. + if (!extMod.hasOwnProperty('__esModule')) { + extension = extMod; } + + plugins = Array.isArray(extension) ? extension : [extension]; + plugins.forEach(function(plugin) { + if (PageConfig.Extension.isDeferred(plugin.id)) { + deferred.push(plugin.id); + ignorePlugins.push(plugin.id); + } + if (PageConfig.Extension.isDisabled(plugin.id)) { + disabled.push(plugin.id); + return; + } + mimeExtensions.push(plugin); + }); } catch (e) { console.error(e); } @@ -68,34 +61,26 @@ function main() { // Handled the registered standard extensions. {{#each jupyterlab_extensions}} try { - if (PageConfig.Extension.isDeferred('{{key}}')) { - deferred.push('{{key}}'); - ignorePlugins.push('{{key}}'); - } - if (PageConfig.Extension.isDisabled('{{@key}}')) { - disabled.push('{{@key}}'); - } else { - extMod = require('{{@key}}/{{this}}'); - extension = extMod.default; - - // Handle CommonJS exports. - if (!extMod.hasOwnProperty('__esModule')) { - extension = extMod; - } + extMod = require('{{@key}}/{{this}}'); + extension = extMod.default; - var list = Array.isArray(extension) ? extension : [extension]; - list.forEach(function(plugin) { - if (PageConfig.Extension.isDeferred(plugin.id)) { - deferred.push(plugin.id); - ignorePlugins.push(plugin.id); - } - if (PageConfig.Extension.isDisabled(plugin.id)) { - disabled.push(plugin.id); - return; - } - register.push(plugin); - }); + // Handle CommonJS exports. + if (!extMod.hasOwnProperty('__esModule')) { + extension = extMod; } + + plugins = Array.isArray(extension) ? extension : [extension]; + plugins.forEach(function(plugin) { + if (PageConfig.Extension.isDeferred(plugin.id)) { + deferred.push(plugin.id); + ignorePlugins.push(plugin.id); + } + if (PageConfig.Extension.isDisabled(plugin.id)) { + disabled.push(plugin.id); + return; + } + register.push(plugin); + }); } catch (e) { console.error(e); } diff --git a/jupyterlab/staging/index.js b/jupyterlab/staging/index.js index 56292397aa15..17e12088e781 100644 --- a/jupyterlab/staging/index.js +++ b/jupyterlab/staging/index.js @@ -30,36 +30,29 @@ function main() { var mimeExtensions = []; var extension; var extMod; + var plugins = []; {{#each jupyterlab_mime_extensions}} try { - if (PageConfig.Extension.isDeferred('{{key}}')) { - deferred.push('{{key}}'); - ignorePlugins.push('{{key}}'); - } - if (PageConfig.Extension.isDisabled('{{@key}}')) { - disabled.push('{{@key}}'); - } else { - extMod = require('{{@key}}/{{this}}'); - extension = extMod.default; - - // Handle CommonJS exports. - if (!extMod.hasOwnProperty('__esModule')) { - extension = extMod; - } + extMod = require('{{@key}}/{{this}}'); + extension = extMod.default; - var list = Array.isArray(extension) ? extension : [extension]; - list.forEach(function(plugin) { - if (PageConfig.Extension.isDeferred(plugin.id)) { - deferred.push(plugin.id); - ignorePlugins.push(plugin.id); - } - if (PageConfig.Extension.isDisabled(plugin.id)) { - disabled.push(plugin.id); - return; - } - mimeExtensions.push(plugin); - }); + // Handle CommonJS exports. + if (!extMod.hasOwnProperty('__esModule')) { + extension = extMod; } + + plugins = Array.isArray(extension) ? extension : [extension]; + plugins.forEach(function(plugin) { + if (PageConfig.Extension.isDeferred(plugin.id)) { + deferred.push(plugin.id); + ignorePlugins.push(plugin.id); + } + if (PageConfig.Extension.isDisabled(plugin.id)) { + disabled.push(plugin.id); + return; + } + mimeExtensions.push(plugin); + }); } catch (e) { console.error(e); } @@ -68,34 +61,26 @@ function main() { // Handled the registered standard extensions. {{#each jupyterlab_extensions}} try { - if (PageConfig.Extension.isDeferred('{{key}}')) { - deferred.push('{{key}}'); - ignorePlugins.push('{{key}}'); - } - if (PageConfig.Extension.isDisabled('{{@key}}')) { - disabled.push('{{@key}}'); - } else { - extMod = require('{{@key}}/{{this}}'); - extension = extMod.default; - - // Handle CommonJS exports. - if (!extMod.hasOwnProperty('__esModule')) { - extension = extMod; - } + extMod = require('{{@key}}/{{this}}'); + extension = extMod.default; - var list = Array.isArray(extension) ? extension : [extension]; - list.forEach(function(plugin) { - if (PageConfig.Extension.isDeferred(plugin.id)) { - deferred.push(plugin.id); - ignorePlugins.push(plugin.id); - } - if (PageConfig.Extension.isDisabled(plugin.id)) { - disabled.push(plugin.id); - return; - } - register.push(plugin); - }); + // Handle CommonJS exports. + if (!extMod.hasOwnProperty('__esModule')) { + extension = extMod; } + + plugins = Array.isArray(extension) ? extension : [extension]; + plugins.forEach(function(plugin) { + if (PageConfig.Extension.isDeferred(plugin.id)) { + deferred.push(plugin.id); + ignorePlugins.push(plugin.id); + } + if (PageConfig.Extension.isDisabled(plugin.id)) { + disabled.push(plugin.id); + return; + } + register.push(plugin); + }); } catch (e) { console.error(e); } From 79f9c427e7561ed5a41797c81ceb4ec3cb7efdff Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Mon, 9 Sep 2019 13:48:50 +0100 Subject: [PATCH 11/20] minor update --- packages/apputils-extension/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/apputils-extension/src/index.ts b/packages/apputils-extension/src/index.ts index 89d365a71378..fab91e37b356 100644 --- a/packages/apputils-extension/src/index.ts +++ b/packages/apputils-extension/src/index.ts @@ -104,13 +104,13 @@ const settings: JupyterFrontEndPlugin = { values: ISettingRegistry.IPlugin[]; }> { const { ids, values } = await app.serviceManager.settings.list(); - const filtered = { + + return { ids: ids.filter(id => !PageConfig.Extension.isDisabled(id)), values: values.filter( ({ id }) => !PageConfig.Extension.isDisabled(id) ) }; - return filtered; } })(); const plugins = (await connector.list()).values; From 0430acd9a684bdb2b4dfe5934c753ac25c98acfc Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Tue, 10 Sep 2019 13:32:55 +0100 Subject: [PATCH 12/20] Add a query to the setting registry list method. --- packages/apputils-extension/src/index.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/apputils-extension/src/index.ts b/packages/apputils-extension/src/index.ts index fab91e37b356..bee364295fbe 100644 --- a/packages/apputils-extension/src/index.ts +++ b/packages/apputils-extension/src/index.ts @@ -99,21 +99,23 @@ const settings: JupyterFrontEndPlugin = { return app.serviceManager.settings.fetch(id); } - async list(): Promise<{ - ids: string[]; - values: ISettingRegistry.IPlugin[]; - }> { - const { ids, values } = await app.serviceManager.settings.list(); + async list( + query: 'active' | 'all' = 'all' + ): Promise<{ ids: string[]; values: ISettingRegistry.IPlugin[] }> { + const { isDeferred, isDisabled } = PageConfig.Extension; + let { ids, values } = await app.serviceManager.settings.list(); + + if (query === 'all') { + return { ids, values }; + } return { - ids: ids.filter(id => !PageConfig.Extension.isDisabled(id)), - values: values.filter( - ({ id }) => !PageConfig.Extension.isDisabled(id) - ) + ids: ids.filter(id => !isDeferred(id) && !isDisabled(id)), + values: values.filter(({ id }) => !isDeferred(id) && !isDisabled(id)) }; } })(); - const plugins = (await connector.list()).values; + const plugins = (await connector.list('active')).values; return new SettingRegistry({ connector, plugins }); }, From b8a6ac9d45d7dad84a478c0494889a129cceaff9 Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Tue, 10 Sep 2019 13:34:07 +0100 Subject: [PATCH 13/20] Add optional query typeparam to data connector. cc: @jasongrout @saulshanabrook --- packages/coreutils/src/dataconnector.ts | 9 ++++++--- packages/coreutils/src/interfaces.ts | 7 +++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/coreutils/src/dataconnector.ts b/packages/coreutils/src/dataconnector.ts index fe24164b182d..637be4960ed1 100644 --- a/packages/coreutils/src/dataconnector.ts +++ b/packages/coreutils/src/dataconnector.ts @@ -16,14 +16,17 @@ import { IDataConnector } from './interfaces'; * ID or filter, but may be set to a different type when an implementation * requires it. Defaults to `string`. * + * @typeparam W - The type of the optional `query` parameter of the `list` + * method. Defaults to `string`. + * * #### Notes * The only abstract method in this class is the `fetch` method, which must be * reimplemented by all subclasses. The `remove` and `save` methods have a * default implementation that returns a promise that will always reject. This * class is a convenience superclass for connectors that only need to `fetch`. */ -export abstract class DataConnector - implements IDataConnector { +export abstract class DataConnector + implements IDataConnector { /** * Retrieve an item from the data connector. * @@ -47,7 +50,7 @@ export abstract class DataConnector * #### Notes * Subclasses should reimplement if they support a back-end that can list. */ - async list(query?: any): Promise<{ ids: V[]; values: T[] }> { + async list(query?: W): Promise<{ ids: V[]; values: T[] }> { throw new Error('DataConnector#list method has not been implemented.'); } diff --git a/packages/coreutils/src/interfaces.ts b/packages/coreutils/src/interfaces.ts index c30b06dc0641..7d70f3387ad9 100644 --- a/packages/coreutils/src/interfaces.ts +++ b/packages/coreutils/src/interfaces.ts @@ -41,8 +41,11 @@ export interface IChangedArgs { * @typeparam V - The basic token applied to a request, conventionally a string * ID or filter, but may be set to a different type when an implementation * requires it. Defaults to `string`. + * + * @typeparam W - The type of the optional `query` parameter of the `list` + * method. Defaults to `string`; */ -export interface IDataConnector { +export interface IDataConnector { /** * Retrieve an item from the data connector. * @@ -69,7 +72,7 @@ export interface IDataConnector { * retrieving the data. The two lists will always be the same size. If there * is no data, this method will succeed with empty `ids` and `values`. */ - list(query?: any): Promise<{ ids: V[]; values: T[] }>; + list(query?: W): Promise<{ ids: V[]; values: T[] }>; /** * Remove a value using the data connector. From 58d12d84064ad27dda80f2d2d7f01b18a75df4e2 Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Tue, 10 Sep 2019 19:36:37 +0100 Subject: [PATCH 14/20] Load deferred plugins manually after the application has restored --- packages/apputils-extension/src/index.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/apputils-extension/src/index.ts b/packages/apputils-extension/src/index.ts index bee364295fbe..bfc482db81c2 100644 --- a/packages/apputils-extension/src/index.ts +++ b/packages/apputils-extension/src/index.ts @@ -91,6 +91,7 @@ const paletteRestorer: JupyterFrontEndPlugin = { const settings: JupyterFrontEndPlugin = { id: '@jupyterlab/apputils-extension:settings', activate: async (app: JupyterFrontEnd): Promise => { + const { isDeferred, isDisabled } = PageConfig.Extension; const connector = new (class SettingConnector extends DataConnector< ISettingRegistry.IPlugin, string @@ -102,7 +103,6 @@ const settings: JupyterFrontEndPlugin = { async list( query: 'active' | 'all' = 'all' ): Promise<{ ids: string[]; values: ISettingRegistry.IPlugin[] }> { - const { isDeferred, isDisabled } = PageConfig.Extension; let { ids, values } = await app.serviceManager.settings.list(); if (query === 'all') { @@ -115,9 +115,24 @@ const settings: JupyterFrontEndPlugin = { }; } })(); - const plugins = (await connector.list('active')).values; - return new SettingRegistry({ connector, plugins }); + // If there are deferred plugins that have settings schemas, do not stall + // returning the setting registry for them; load them manually after the + // application has restored in order to make sure their settings exist in + // the setting registry, even if nothing else has activated them. + void app.restored.then(async () => { + const plugins = await connector.list('all'); + plugins.ids.forEach(id => { + if (isDeferred(id) && !isDisabled(id)) { + void app.activatePlugin(id); + } + }); + }); + + return new SettingRegistry({ + connector, + plugins: (await connector.list('active')).values + }); }, autoStart: true, provides: ISettingRegistry From cd82a4a7f771fe96a71cf7ce6c1fbe503de18ffd Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Wed, 11 Sep 2019 12:57:14 +0100 Subject: [PATCH 15/20] Update deferred settings load logic. --- packages/apputils-extension/src/index.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/apputils-extension/src/index.ts b/packages/apputils-extension/src/index.ts index bfc482db81c2..53338da495b4 100644 --- a/packages/apputils-extension/src/index.ts +++ b/packages/apputils-extension/src/index.ts @@ -116,23 +116,30 @@ const settings: JupyterFrontEndPlugin = { } })(); + const registry = new SettingRegistry({ + connector, + plugins: (await connector.list('active')).values + }); + // If there are deferred plugins that have settings schemas, do not stall // returning the setting registry for them; load them manually after the // application has restored in order to make sure their settings exist in // the setting registry, even if nothing else has activated them. + // + // The plugins are activated before their settings are loaded into the + // registry in order to guarantee any setting transformations that exist in + // the plugin are registered with the setting registry. void app.restored.then(async () => { const plugins = await connector.list('all'); - plugins.ids.forEach(id => { + plugins.ids.forEach(async id => { if (isDeferred(id) && !isDisabled(id)) { - void app.activatePlugin(id); + await app.activatePlugin(id); + void registry.load(id); } }); }); - return new SettingRegistry({ - connector, - plugins: (await connector.list('active')).values - }); + return registry; }, autoStart: true, provides: ISettingRegistry From 74a7a76bef13a2d8cd0d46347ebd2732d180ed42 Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Wed, 11 Sep 2019 19:10:47 +0100 Subject: [PATCH 16/20] Shorten setting registry transform timeout. --- packages/coreutils/src/settingregistry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/coreutils/src/settingregistry.ts b/packages/coreutils/src/settingregistry.ts index e8a7a9f54791..bacf7feedc1e 100644 --- a/packages/coreutils/src/settingregistry.ts +++ b/packages/coreutils/src/settingregistry.ts @@ -33,7 +33,7 @@ const copy = JSONExt.deepCopy; * will wait before timing out if it requires a transformation that has not been * registered. */ -const DEFAULT_TRANSFORM_TIMEOUT = 7000; +const DEFAULT_TRANSFORM_TIMEOUT = 1000; /** * The ASCII record separator character. From 812f0bc94292c080da1bc48b8afa16f5195a7481 Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Wed, 11 Sep 2019 19:58:25 +0100 Subject: [PATCH 17/20] Try to load deferred extensions settings after the application has restored. --- packages/apputils-extension/src/index.ts | 26 +++++++++++++----------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/apputils-extension/src/index.ts b/packages/apputils-extension/src/index.ts index 53338da495b4..ff4791db5f0a 100644 --- a/packages/apputils-extension/src/index.ts +++ b/packages/apputils-extension/src/index.ts @@ -121,20 +121,22 @@ const settings: JupyterFrontEndPlugin = { plugins: (await connector.list('active')).values }); - // If there are deferred plugins that have settings schemas, do not stall - // returning the setting registry for them; load them manually after the - // application has restored in order to make sure their settings exist in - // the setting registry, even if nothing else has activated them. - // - // The plugins are activated before their settings are loaded into the - // registry in order to guarantee any setting transformations that exist in - // the plugin are registered with the setting registry. + // If there are plugins that have schemas that are not in the setting + // registry after the application has restored, try to load them manually. void app.restored.then(async () => { const plugins = await connector.list('all'); - plugins.ids.forEach(async id => { - if (isDeferred(id) && !isDisabled(id)) { - await app.activatePlugin(id); - void registry.load(id); + plugins.ids.forEach(async (id, index) => { + if (isDisabled(id) || id in registry.plugins) { + return; + } + + try { + await registry.load(id); + } catch (error) { + console.warn(`Settings failed to load for ${id}`, error); + if (plugins.values[index].schema['jupyter.lab.transform']) { + console.warn(`This may happen if {autoStart: false} in ${id}`); + } } }); }); From cf6955a975bb8d870f794a38aa37fec03eb93ac4 Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Thu, 12 Sep 2019 11:40:18 +0100 Subject: [PATCH 18/20] Update doc strings --- packages/coreutils/src/pageconfig.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/coreutils/src/pageconfig.ts b/packages/coreutils/src/pageconfig.ts index b682a5540fb1..3d85754de1a0 100644 --- a/packages/coreutils/src/pageconfig.ts +++ b/packages/coreutils/src/pageconfig.ts @@ -196,6 +196,14 @@ export namespace PageConfig { * The namespace for page config `Extension` functions. */ export namespace Extension { + /** + * Populate an array from page config. + * + * @param key - The page config key (e.g., `deferredExtensions`). + * + * #### Notes + * This is intended for `deferredExtensions` and `disabledExtensions`. + */ function populate(key: string): { raw: string; rule: RegExp }[] { try { const raw = getOption(key); @@ -210,14 +218,30 @@ export namespace PageConfig { return []; } + /** + * The collection of deferred extensions in page config. + */ export const deferred = populate('deferredExtensions'); + /** + * The collection of disabled extensions in page config. + */ export const disabled = populate('disabledExtensions'); + /** + * Returns whether a plugin is deferred. + * + * @param id - The plugin ID. + */ export function isDeferred(id: string): boolean { return deferred.some(val => val.raw === id || val.rule.test(id)); } + /** + * Returns whether a plugin is disabled. + * + * @param id - The plugin ID. + */ export function isDisabled(id: string): boolean { return disabled.some(val => val.raw === id || val.rule.test(id)); } From 750dcfe60bc3b163966d36cfb263d74581b6bfad Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Thu, 12 Sep 2019 11:55:56 +0100 Subject: [PATCH 19/20] Update message. --- packages/apputils-extension/src/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/apputils-extension/src/index.ts b/packages/apputils-extension/src/index.ts index ff4791db5f0a..acea803d3f82 100644 --- a/packages/apputils-extension/src/index.ts +++ b/packages/apputils-extension/src/index.ts @@ -133,9 +133,12 @@ const settings: JupyterFrontEndPlugin = { try { await registry.load(id); } catch (error) { - console.warn(`Settings failed to load for ${id}`, error); + console.warn(`Settings failed to load for (${id})`, error); if (plugins.values[index].schema['jupyter.lab.transform']) { - console.warn(`This may happen if {autoStart: false} in ${id}`); + console.warn( + `This may happen if {autoStart: false} in (${id}) ` + + `or if it is one of the deferredExtensions in page config.` + ); } } }); From 531b19084ea771c555f7defdfe28734ba5648e6d Mon Sep 17 00:00:00 2001 From: "Afshin T. Darian" Date: Wed, 4 Dec 2019 14:46:43 -0600 Subject: [PATCH 20/20] Move setting connector to its own file and clarify loading deferred plugin settings logic in a comment. --- packages/apputils-extension/src/index.ts | 33 +++----------- .../src/settingconnector.ts | 44 +++++++++++++++++++ 2 files changed, 51 insertions(+), 26 deletions(-) create mode 100644 packages/apputils-extension/src/settingconnector.ts diff --git a/packages/apputils-extension/src/index.ts b/packages/apputils-extension/src/index.ts index acea803d3f82..4cbd210dd5dd 100644 --- a/packages/apputils-extension/src/index.ts +++ b/packages/apputils-extension/src/index.ts @@ -20,7 +20,6 @@ import { } from '@jupyterlab/apputils'; import { - DataConnector, Debouncer, ISettingRegistry, IStateDB, @@ -39,6 +38,8 @@ import { DisposableDelegate } from '@phosphor/disposable'; import { Palette } from './palette'; +import { SettingConnector } from './settingconnector'; + import { themesPlugin, themesPaletteMenuPlugin } from './themeplugins'; /** @@ -91,30 +92,8 @@ const paletteRestorer: JupyterFrontEndPlugin = { const settings: JupyterFrontEndPlugin = { id: '@jupyterlab/apputils-extension:settings', activate: async (app: JupyterFrontEnd): Promise => { - const { isDeferred, isDisabled } = PageConfig.Extension; - const connector = new (class SettingConnector extends DataConnector< - ISettingRegistry.IPlugin, - string - > { - fetch(id: string): Promise { - return app.serviceManager.settings.fetch(id); - } - - async list( - query: 'active' | 'all' = 'all' - ): Promise<{ ids: string[]; values: ISettingRegistry.IPlugin[] }> { - let { ids, values } = await app.serviceManager.settings.list(); - - if (query === 'all') { - return { ids, values }; - } - - return { - ids: ids.filter(id => !isDeferred(id) && !isDisabled(id)), - values: values.filter(({ id }) => !isDeferred(id) && !isDisabled(id)) - }; - } - })(); + const { isDisabled } = PageConfig.Extension; + const connector = new SettingConnector(app.serviceManager.settings); const registry = new SettingRegistry({ connector, @@ -122,7 +101,9 @@ const settings: JupyterFrontEndPlugin = { }); // If there are plugins that have schemas that are not in the setting - // registry after the application has restored, try to load them manually. + // registry after the application has restored, try to load them manually + // because otherwise, its settings will never become available in the + // setting registry. void app.restored.then(async () => { const plugins = await connector.list('all'); plugins.ids.forEach(async (id, index) => { diff --git a/packages/apputils-extension/src/settingconnector.ts b/packages/apputils-extension/src/settingconnector.ts new file mode 100644 index 000000000000..b1192c57a799 --- /dev/null +++ b/packages/apputils-extension/src/settingconnector.ts @@ -0,0 +1,44 @@ +import { + DataConnector, + IDataConnector, + ISettingRegistry, + PageConfig +} from '@jupyterlab/coreutils'; + +/** + * A data connector for fetching settings. + * + * #### Notes + * This connector adds a query parameter to the base services setting manager. + */ +export class SettingConnector extends DataConnector< + ISettingRegistry.IPlugin, + string +> { + constructor(connector: IDataConnector) { + super(); + this._connector = connector; + } + + fetch(id: string): Promise { + return this._connector.fetch(id); + } + + async list( + query: 'active' | 'all' = 'all' + ): Promise<{ ids: string[]; values: ISettingRegistry.IPlugin[] }> { + const { isDeferred, isDisabled } = PageConfig.Extension; + let { ids, values } = await this._connector.list(); + + if (query === 'all') { + return { ids, values }; + } + + return { + ids: ids.filter(id => !isDeferred(id) && !isDisabled(id)), + values: values.filter(({ id }) => !isDeferred(id) && !isDisabled(id)) + }; + } + + private _connector: IDataConnector; +}