Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@ckeditor/ckeditor5-core

Package Overview
Dependencies
Maintainers
1
Versions
705
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ckeditor/ckeditor5-core - npm Package Compare versions

Comparing version 0.6.0 to 0.7.0

.github/PULL_REQUEST_TEMPLATE.md

10

package.json
{
"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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc