@ckeditor/ckeditor5-core
Advanced tools
Comparing version 0.6.0 to 0.7.0
{ | ||
"name": "@ckeditor/ckeditor5-core", | ||
"version": "0.6.0", | ||
"version": "0.7.0", | ||
"description": "", | ||
"keywords": [], | ||
"dependencies": { | ||
"@ckeditor/ckeditor5-engine": "*", | ||
"@ckeditor/ckeditor5-utils": "*" | ||
"@ckeditor/ckeditor5-engine": "^0.8.0", | ||
"@ckeditor/ckeditor5-utils": "^0.8.0" | ||
}, | ||
"devDependencies": { | ||
"@ckeditor/ckeditor5-dev-lint": "^2.0.0", | ||
"@ckeditor/ckeditor5-ui": "*", | ||
"@ckeditor/ckeditor5-dev-lint": "^2.0.2", | ||
"@ckeditor/ckeditor5-ui": "^0.7.1", | ||
"gulp": "^3.9.0", | ||
@@ -14,0 +14,0 @@ "guppy-pre-commit": "^0.4.0" |
@@ -38,3 +38,3 @@ /** | ||
* @readonly | ||
* @member {utils.Config} | ||
* @member {module:utils/config~Config} | ||
*/ | ||
@@ -41,0 +41,0 @@ this.config = new Config( config ); |
@@ -19,7 +19,12 @@ /** | ||
/** | ||
* Creates an instance of the PluginCollection class, initializing it with a set of plugins. | ||
* Creates an instance of the PluginCollection class. | ||
* Allows loading and initializing plugins and their dependencies. | ||
* | ||
* @param {module:core/editor/editor~Editor} editor | ||
* @param {Array.<Function>} [availablePlugins] Plugins (constructors) which the collection will be able to use | ||
* when {@link module:core/plugin~PluginCollection#load} is used with plugin names (strings, instead of constructors). | ||
* Usually, the editor will pass its built-in plugins to the collection so they can later be | ||
* used in `config.plugins` or `config.removePlugins` by names. | ||
*/ | ||
constructor( editor ) { | ||
constructor( editor, availablePlugins = [] ) { | ||
/** | ||
@@ -32,6 +37,22 @@ * @protected | ||
/** | ||
* Map of plugin constructors which can be retrieved by their names. | ||
* | ||
* @protected | ||
* @member {Map.<String|Function,Function>} module:core/plugin~PluginCollection#_availablePlugins | ||
*/ | ||
this._availablePlugins = new Map(); | ||
/** | ||
* @protected | ||
* @member {Map} module:core/plugin~PluginCollection#_plugins | ||
*/ | ||
this._plugins = new Map(); | ||
for ( const PluginConstructor of availablePlugins ) { | ||
this._availablePlugins.set( PluginConstructor, PluginConstructor ); | ||
if ( PluginConstructor.pluginName ) { | ||
this._availablePlugins.set( PluginConstructor.pluginName, PluginConstructor ); | ||
} | ||
} | ||
} | ||
@@ -61,10 +82,14 @@ | ||
/** | ||
* Loads a set of plugins and add them to the collection. | ||
* Loads a set of plugins and adds them to the collection. | ||
* | ||
* @param {Function[]} plugins An array of {@link module:core/plugin~Plugin plugin constructors}. | ||
* @param {Array.<Function|String>} plugins An array of {@link module:core/plugin~Plugin plugin constructors} | ||
* or {@link module:core/plugin~Plugin.pluginName plugin names}. The second option (names) work only if | ||
* `availablePlugins` were passed to the {@link #constructor}. | ||
* @param {Array.<String|Function>} [removePlugins] Names of plugins or plugin constructors | ||
* which should not be loaded (despite being specified in the `plugins` array). | ||
* @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. | ||
*/ | ||
load( plugins ) { | ||
load( plugins, removePlugins = [] ) { | ||
const that = this; | ||
@@ -75,6 +100,35 @@ const editor = this._editor; | ||
return Promise.all( plugins.map( loadPlugin ) ) | ||
const pluginConstructors = mapToAvailableConstructors( plugins ); | ||
const removePluginConstructors = mapToAvailableConstructors( removePlugins ); | ||
const missingPlugins = getMissingPluginNames( plugins ); | ||
if ( missingPlugins ) { | ||
// TODO update this error docs with links to docs because it will be a frequent problem. | ||
/** | ||
* Some plugins are not available and could not be loaded. | ||
* | ||
* Plugin classes (constructors) need to be provided to the editor before they can be loaded by name. | ||
* This is usually done by the builder by setting the {@link module:core/editor/editor~Editor.build} | ||
* property. | ||
* | ||
* @error plugincollection-plugin-not-found | ||
* @param {Array.<String>} plugins The name of the plugins which could not be loaded. | ||
*/ | ||
const errorMsg = 'plugincollection-plugin-not-found: Some plugins are not available and could not be loaded.'; | ||
// Log the error so it's more visible on the console. Hopefuly, for better DX. | ||
log.error( errorMsg, { plugins: missingPlugins } ); | ||
return Promise.reject( new CKEditorError( errorMsg, { plugins: missingPlugins } ) ); | ||
} | ||
return Promise.all( pluginConstructors.map( loadPlugin ) ) | ||
.then( () => loaded ); | ||
function loadPlugin( PluginConstructor ) { | ||
if ( removePluginConstructors.includes( PluginConstructor ) ) { | ||
return; | ||
} | ||
// The plugin is already loaded or being loaded - do nothing. | ||
@@ -106,3 +160,22 @@ if ( that.get( PluginConstructor ) || loading.has( PluginConstructor ) ) { | ||
if ( PluginConstructor.requires ) { | ||
PluginConstructor.requires.forEach( loadPlugin ); | ||
PluginConstructor.requires.forEach( ( RequiredPluginConstructorOrName ) => { | ||
const RequiredPluginConstructor = getPluginConstructor( RequiredPluginConstructorOrName ); | ||
if ( removePlugins.includes( RequiredPluginConstructor ) ) { | ||
/** | ||
* Cannot load a plugin because one of its dependencies is listed in the `removePlugins` option. | ||
* | ||
* @error plugincollection-required | ||
* @param {Function} plugin The required plugin. | ||
* @param {Function} requiredBy The parent plugin. | ||
*/ | ||
throw new CKEditorError( | ||
'plugincollection-required: Cannot load a plugin because one of its dependencies is listed in' + | ||
'the `removePlugins` option.', | ||
{ plugin: RequiredPluginConstructor, requiredBy: PluginConstructor } | ||
); | ||
} | ||
loadPlugin( RequiredPluginConstructor ); | ||
} ); | ||
} | ||
@@ -118,2 +191,10 @@ | ||
function getPluginConstructor( PluginConstructorOrName ) { | ||
if ( typeof PluginConstructorOrName == 'function' ) { | ||
return PluginConstructorOrName; | ||
} | ||
return that._availablePlugins.get( PluginConstructorOrName ); | ||
} | ||
function assertIsPlugin( PluginConstructor ) { | ||
@@ -133,2 +214,20 @@ if ( !( PluginConstructor.prototype instanceof Plugin ) ) { | ||
} | ||
function getMissingPluginNames( plugins ) { | ||
const missingPlugins = []; | ||
for ( const pluginNameOrConstructor of plugins ) { | ||
if ( !getPluginConstructor( pluginNameOrConstructor ) ) { | ||
missingPlugins.push( pluginNameOrConstructor ); | ||
} | ||
} | ||
return missingPlugins.length ? missingPlugins : null; | ||
} | ||
function mapToAvailableConstructors( plugins ) { | ||
return plugins | ||
.map( pluginNameOrConstructor => getPluginConstructor( pluginNameOrConstructor ) ) | ||
.filter( PluginConstructor => !!PluginConstructor ); | ||
} | ||
} | ||
@@ -135,0 +234,0 @@ |
@@ -13,4 +13,4 @@ /** | ||
let editor; | ||
let PluginA, PluginB, PluginC, PluginD, PluginE, PluginF, PluginG, PluginH, PluginI, PluginX; | ||
let editor, availablePlugins; | ||
let PluginA, PluginB, PluginC, PluginD, PluginE, PluginF, PluginG, PluginH, PluginI, PluginJ, PluginK, PluginX; | ||
class TestError extends Error {} | ||
@@ -32,2 +32,4 @@ class ChildPlugin extends Plugin {} | ||
PluginI = createPlugin( 'I' ); | ||
PluginJ = createPlugin( 'J' ); | ||
PluginK = createPlugin( 'K' ); | ||
PluginX = class extends Plugin { | ||
@@ -46,2 +48,4 @@ constructor( editor ) { | ||
PluginH.requires = [ PluginI ]; | ||
PluginJ.requires = [ 'K' ]; | ||
PluginK.requires = [ PluginA ]; | ||
@@ -52,5 +56,22 @@ editor = new Editor(); | ||
describe( 'PluginCollection', () => { | ||
beforeEach( () => { | ||
availablePlugins = [ | ||
PluginA, | ||
PluginB, | ||
PluginC, | ||
PluginD, | ||
PluginE, | ||
PluginF, | ||
PluginG, | ||
PluginH, | ||
PluginI, | ||
PluginJ, | ||
PluginK, | ||
PluginX | ||
]; | ||
} ); | ||
describe( 'load()', () => { | ||
it( 'should not fail when trying to load 0 plugins (empty array)', () => { | ||
let plugins = new PluginCollection( editor ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
@@ -64,3 +85,3 @@ return plugins.load( [] ) | ||
it( 'should add collection items for loaded plugins', () => { | ||
let plugins = new PluginCollection( editor ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
@@ -76,4 +97,16 @@ return plugins.load( [ PluginA, PluginB ] ) | ||
it( 'should add collection items for loaded plugins using plugin names', () => { | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
return plugins.load( [ 'A', 'B' ] ) | ||
.then( () => { | ||
expect( getPlugins( plugins ).length ).to.equal( 2 ); | ||
expect( plugins.get( 'A' ) ).to.be.an.instanceof( PluginA ); | ||
expect( plugins.get( 'B' ) ).to.be.an.instanceof( PluginB ); | ||
} ); | ||
} ); | ||
it( 'should load dependency plugins', () => { | ||
let plugins = new PluginCollection( editor ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
let spy = sinon.spy( plugins, '_add' ); | ||
@@ -92,4 +125,19 @@ | ||
it( 'should load dependency plugins defined by plugin names', () => { | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
let spy = sinon.spy( plugins, '_add' ); | ||
return plugins.load( [ 'J' ] ) | ||
.then( ( loadedPlugins ) => { | ||
expect( getPlugins( plugins ).length ).to.equal( 3 ); | ||
expect( getPluginNames( getPluginsFromSpy( spy ) ) ) | ||
.to.deep.equal( [ 'A', 'K', 'J' ], 'order by plugins._add()' ); | ||
expect( getPluginNames( loadedPlugins ) ) | ||
.to.deep.equal( [ 'A', 'K', 'J' ], 'order by returned value' ); | ||
} ); | ||
} ); | ||
it( 'should be ok when dependencies are loaded first', () => { | ||
let plugins = new PluginCollection( editor ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
let spy = sinon.spy( plugins, '_add' ); | ||
@@ -109,3 +157,3 @@ | ||
it( 'should load deep dependency plugins', () => { | ||
let plugins = new PluginCollection( editor ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
let spy = sinon.spy( plugins, '_add' ); | ||
@@ -126,3 +174,3 @@ | ||
it( 'should handle cross dependency plugins', () => { | ||
let plugins = new PluginCollection( editor ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
let spy = sinon.spy( plugins, '_add' ); | ||
@@ -143,3 +191,3 @@ | ||
it( 'should load grand child classes', () => { | ||
let plugins = new PluginCollection( editor ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
@@ -153,3 +201,3 @@ return plugins.load( [ PluginG ] ) | ||
it( 'should set the `editor` property on loaded plugins', () => { | ||
let plugins = new PluginCollection( editor ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
@@ -166,3 +214,3 @@ return plugins.load( [ PluginA, PluginB ] ) | ||
let plugins = new PluginCollection( editor ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
@@ -184,8 +232,10 @@ return plugins.load( [ PluginA, PluginX, PluginB ] ) | ||
it( 'should reject when loading a module which is not a plugin', () => { | ||
class Y {} | ||
availablePlugins.push( Y ); | ||
let logSpy = testUtils.sinon.stub( log, 'error' ); | ||
let plugins = new PluginCollection( editor ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
class Y {} | ||
return plugins.load( [ Y ] ) | ||
@@ -204,2 +254,100 @@ // Throw here, so if by any chance plugins.load() was resolved correctly catch() will be stil executed. | ||
} ); | ||
it( 'should reject when loading non-existent plugin', () => { | ||
let logSpy = testUtils.sinon.stub( log, 'error' ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
return plugins.load( [ 'NonExistentPlugin' ] ) | ||
// Throw here, so if by any chance plugins.load() was resolved correctly catch() will be stil executed. | ||
.then( () => { | ||
throw new Error( 'Test error: this promise should not be resolved successfully' ); | ||
} ) | ||
.catch( ( err ) => { | ||
expect( err ).to.be.an.instanceof( CKEditorError ); | ||
expect( err.message ).to.match( /^plugincollection-plugin-not-found/ ); | ||
sinon.assert.calledOnce( logSpy ); | ||
expect( logSpy.args[ 0 ][ 0 ] ).to.match( /^plugincollection-plugin-not-found:/ ); | ||
} ); | ||
} ); | ||
it( 'should load chosen plugins (plugins and removePlugins are constructors)', () => { | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
return plugins.load( [ PluginA, PluginB, PluginC ], [ PluginA ] ) | ||
.then( () => { | ||
expect( getPlugins( plugins ).length ).to.equal( 2 ); | ||
expect( plugins.get( PluginB ) ).to.be.an.instanceof( PluginB ); | ||
expect( plugins.get( PluginC ) ).to.be.an.instanceof( PluginC ); | ||
} ); | ||
} ); | ||
it( 'should load chosen plugins (plugins are constructors, removePlugins are names)', () => { | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
return plugins.load( [ PluginA, PluginB, PluginC ], [ 'A' ] ) | ||
.then( () => { | ||
expect( getPlugins( plugins ).length ).to.equal( 2 ); | ||
expect( plugins.get( PluginB ) ).to.be.an.instanceof( PluginB ); | ||
expect( plugins.get( PluginC ) ).to.be.an.instanceof( PluginC ); | ||
} ); | ||
} ); | ||
it( 'should load chosen plugins (plugins and removePlugins are names)', () => { | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
return plugins.load( [ 'A', 'B', 'C' ], [ 'A' ] ) | ||
.then( () => { | ||
expect( getPlugins( plugins ).length ).to.equal( 2 ); | ||
expect( plugins.get( PluginB ) ).to.be.an.instanceof( PluginB ); | ||
expect( plugins.get( PluginC ) ).to.be.an.instanceof( PluginC ); | ||
} ); | ||
} ); | ||
it( 'should load chosen plugins (plugins are names, removePlugins are constructors)', () => { | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
return plugins.load( [ 'A', 'B', 'C' ], [ PluginA ] ) | ||
.then( () => { | ||
expect( getPlugins( plugins ).length ).to.equal( 2 ); | ||
expect( plugins.get( PluginB ) ).to.be.an.instanceof( PluginB ); | ||
expect( plugins.get( PluginC ) ).to.be.an.instanceof( PluginC ); | ||
} ); | ||
} ); | ||
it( 'should load chosen plugins (plugins are names, removePlugins contains an anonymous plugin)', () => { | ||
class AnonymousPlugin extends Plugin {} | ||
let plugins = new PluginCollection( editor, [ AnonymousPlugin ].concat( availablePlugins ) ); | ||
return plugins.load( [ AnonymousPlugin, 'A', 'B' ], [ AnonymousPlugin ] ) | ||
.then( () => { | ||
expect( getPlugins( plugins ).length ).to.equal( 2 ); | ||
expect( plugins.get( PluginA ) ).to.be.an.instanceof( PluginA ); | ||
expect( plugins.get( PluginB ) ).to.be.an.instanceof( PluginB ); | ||
} ); | ||
} ); | ||
it( 'should reject when loaded plugin requires not allowed plugins', () => { | ||
let logSpy = testUtils.sinon.stub( log, 'error' ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
return plugins.load( [ PluginA, PluginB, PluginC, PluginD ], [ PluginA, PluginB ] ) | ||
// Throw here, so if by any chance plugins.load() was resolved correctly catch() will be stil executed. | ||
.then( () => { | ||
throw new Error( 'Test error: this promise should not be resolved successfully' ); | ||
} ) | ||
.catch( ( err ) => { | ||
expect( err ).to.be.an.instanceof( CKEditorError ); | ||
expect( err.message ).to.match( /^plugincollection-required/ ); | ||
expect( logSpy.calledTwice ).to.equal( true ); | ||
} ); | ||
} ); | ||
} ); | ||
@@ -209,6 +357,8 @@ | ||
it( 'retrieves plugin by its constructor', () => { | ||
let plugins = new PluginCollection( editor ); | ||
class SomePlugin extends Plugin {} | ||
availablePlugins.push( SomePlugin ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
return plugins.load( [ SomePlugin ] ) | ||
@@ -221,7 +371,9 @@ .then( () => { | ||
it( 'retrieves plugin by its name and constructor', () => { | ||
let plugins = new PluginCollection( editor ); | ||
class SomePlugin extends Plugin {} | ||
SomePlugin.pluginName = 'foo/bar'; | ||
availablePlugins.push( SomePlugin ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
return plugins.load( [ SomePlugin ] ) | ||
@@ -237,3 +389,3 @@ .then( () => { | ||
it( 'exists', () => { | ||
let plugins = new PluginCollection( editor ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
@@ -244,4 +396,2 @@ expect( plugins ).to.have.property( Symbol.iterator ); | ||
it( 'returns only plugins by constructors', () => { | ||
let plugins = new PluginCollection( editor ); | ||
class SomePlugin1 extends Plugin {} | ||
@@ -251,2 +401,7 @@ class SomePlugin2 extends Plugin {} | ||
availablePlugins.push( SomePlugin1 ); | ||
availablePlugins.push( SomePlugin2 ); | ||
let plugins = new PluginCollection( editor, availablePlugins ); | ||
return plugins.load( [ SomePlugin1, SomePlugin2 ] ) | ||
@@ -253,0 +408,0 @@ .then( () => { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Wildcard dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 2 instances in 1 package
145663
73
2854
0
+ Added@ckeditor/ckeditor5-engine@0.8.0(transitive)
+ Added@ckeditor/ckeditor5-utils@0.8.0(transitive)
- Removed@ckeditor/ckeditor5-core@43.3.1(transitive)
- Removed@ckeditor/ckeditor5-engine@43.3.1(transitive)
- Removed@ckeditor/ckeditor5-ui@43.3.1(transitive)
- Removed@ckeditor/ckeditor5-utils@43.3.1(transitive)
- Removed@ckeditor/ckeditor5-watchdog@43.3.1(transitive)
- Removedcolor-convert@2.0.1(transitive)
- Removedcolor-name@1.1.4(transitive)
- Removedcolor-parse@1.4.2(transitive)
- Removedlodash-es@4.17.21(transitive)
- Removedvanilla-colorful@0.7.2(transitive)