Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

T/49 #70

Merged
merged 17 commits into from
Mar 1, 2017
Merged

T/49 #70

merged 17 commits into from
Mar 1, 2017

Conversation

pomek
Copy link
Member

@pomek pomek commented Feb 28, 2017

Suggested merge commit message (convention)

Feature: PluginCollection - support for loading plugin by names. Closes ckeditor/ckeditor5#2873.

Introduced a new parameter for PluginCollection.load() - removePlugins.
The parameter is a list which defines plugins that won't be loaded.


Additional information

@@ -45,7 +45,7 @@ export default class Editor {
* @readonly
* @member {module:core/plugin~PluginCollection}
*/
this.plugins = new PluginCollection( this );
this.plugins = new PluginCollection( this, this.config.get( 'plugins' ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not according to the spec (https://github.com/ckeditor/ckeditor5-core/issues/49#issuecomment-282299034).

available plugins !== config.plugins

There are no available plugins build in the editor yet because it's a separate ticket: https://github.com/ckeditor/ckeditor5-core/issues/67. So you can revert this change.


// Save available plugins.
for ( const plugin of availablePlugins ) {
this._availablePlugins.set( plugin.pluginName, plugin );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plugin name is optional.

.catch( ( err ) => {
/**
* It was not possible to load the plugin.
*
* @error plugincollection-load
* @param {String} plugin The name of the plugin that could not be loaded.
*/
log.error( 'plugincollection-load: It was not possible to load the plugin.', { plugin: PluginConstructor } );
log.error( 'plugincollection-load: It was not possible to load the plugin.', { plugin: PluginConstructorOrName } );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I see correctly that this error will be thrown if a plugin cannot be load because it cannot be found by its name? This should be a separate, more meaningful error.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* @param {*} plugin The dependency constructor which is meant to be loaded as a plugin.
* @param {*} requiredBy The parent constructor which is meant to be loaded as a plugin.
*/
// jscs:disable maximumLineLength
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't disable the linter. Just fix the code (split the text).

@@ -29,9 +30,24 @@ export default class PluginCollection {

/**
* @protected
* @member {Map.<String,module:core/plugin~Plugin>} module:core/plugin~PluginCollection#_availablePlugins
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The key may not be a string AFAICS below. It may be a function too.

@@ -110,6 +166,29 @@ export default class PluginCollection {
} );
}

function getPluginConstructor( PluginConstructorOrName ) {
if ( typeof PluginConstructorOrName === 'function' ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use == after typeof.


if ( !PluginConstructor ) {
/**
* The loaded plugin module is not available.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update to:

The plugin cannot be loaded by name.

Plugin classes need to be provided to the editor before they can be loaded by name. This is usually done by the builder.

TODO update this error with links to docs because it will be a very frequent problem.

@error plugincollection-plugin-not-found
@param {String} pluginName The name of the plugin which could not be loaded.

@Reinmar
Copy link
Member

Reinmar commented Feb 28, 2017

OK, I checked the code more carefully and there are couple of things which should be refined here.

The implementation became too complex now, mostly because unification of what kind of plugin (string or constructor) was passed to load() is done too late.

  1. The plugins array should be turned into an array of constructors ASAP. This way you'll avoid having PluginConstructor__OrName__ all around that code.
  2. There should be no error if you executed load( [ PluginA ], [ 'NonExistentPlugin' ] ). The error which would be thrown now makes no sense in this case. We can simply ignore names for which we can't match any plugins.
  3. Don't modify the removePlugins variable in load(). Create a new one which will better tell what it is. Also, from what I can see it can be just an array of constructors. It doesn't need to contain anything more.

Besides, you had a missing code coverage (which I fixed in my last commit).

@pomek
Copy link
Member Author

pomek commented Mar 1, 2017

Let's try once again :D

image

.map( ( item ) => getPluginConstructor( item ) )
.filter( ( PluginConstructor ) => !!PluginConstructor );

// Check whether a length of passed array with plugins is the same like an array with mapped plugins.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you noticed yourself, this comment needs to be refined :D

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides, I don't like splitting the filtering which is done above from the check which is done below because it's a single piece of logic – can't find a plugin – throw.

OTOH, I can see that when using filter() it would be hard to reject the promise and stop the execution, so the check makes sense to be separated. So let's keep it like that, but please move it to a separate function. If you'll name this function intuitively, you won't need that comment :P.

The code in here could look like this:

const missingPlugins = getMissingPlugins( plugins, pluginConstructorsToLoad );

if ( missingPlugins ) {
    return Promise.reject();
}

// Check whether a length of passed array with plugins is the same like an array with mapped plugins.
if ( pluginConstructorsToLoad.length !== plugins.length ) {
// Extract names of plugins which cannot be loaded.
const notFoundPlugins = plugins.filter( ( item ) => !pluginConstructorsToLoad.includes( item ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this is a wrong way of getting missing plugins. In plugins you may have strings and in pluginConstructorsToLoad you will have constructors. So checking if the former is in the latter doesn't make sense. Use getPluginConstructor() again.

PluginConstructor.requires.forEach( ( RequiredPluginConstructorOrName ) => {
const RequiredPluginConstructor = getPluginConstructor( RequiredPluginConstructorOrName );

if ( !RequiredPluginConstructor ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unnecessary in this PR. It doesn't make sense for a to require other plugins by their names.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PS. I meant that this may be a possible feature in the future, but it's not part of the ticket for which you're making a PR, so it shouldn't be included in it.

.then( () => loaded );

function loadPlugin( PluginConstructorOrName ) {
function loadPlugin( PluginConstructor ) {
// Don't load the plugin if it cannot be loaded.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is unnecessary.

*
* @param {module:core/editor/editor~Editor} editor
* @param {Array.<Function>} availablePlugins Plugins (constructor) which the collection will be able to use
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is optional.

* @returns {Promise} A promise which gets resolved once all plugins are loaded and available into the
* collection.
* @param {Array.<module:core/plugin~Plugin>} returns.loadedPlugins The array of loaded plugins.
* @returns {Promise.<Array.<module:core/plugin~Plugin>>} returns.loadedPlugins The array of loaded plugins.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is optional.

const missingPlugins = getMissingPlugins( plugins );

if ( missingPlugins ) {
log.error( 'plugincollection-load: It was not possible to load the plugins.', { plugins: missingPlugins } );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be the same error as the rejection below. A single error means one thing. Here, you tried to reuse an error defined in another place, but you changed its meaning (from one plugin to multiple plugins and from "can't be loaded due to an error" to "can't be loaded because wasn't provided").

return Promise.all( plugins.map( loadPlugin ) )
// In order to avoid using plugin names or constructors alternatively, we map all passed plugin as plugins constructors.
const pluginConstructorsToLoad = plugins
.map( ( item ) => getPluginConstructor( item ) )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not an item. It's a plugin name or constructor.

@Reinmar
Copy link
Member

Reinmar commented Mar 1, 2017

Feature: Added support for loading plugins by name and the "removePlugins" option. Closes #49.

@Reinmar Reinmar merged commit dfee52e into master Mar 1, 2017
@Reinmar Reinmar deleted the t/49 branch March 1, 2017 18:34
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
2 participants