Skip to content

Commit

Permalink
Merge pull request #10300 from marcellofuschi/i/4959
Browse files Browse the repository at this point in the history
Fix (utils): Properly stringify objects containing circular references in `CKEditorError`. Closes #4959.

Thanks to @marcellofuschi!
  • Loading branch information
dawidurbanski committed Oct 14, 2021
2 parents 964afbe + dac2119 commit 6741143
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 3 deletions.
41 changes: 38 additions & 3 deletions packages/ckeditor5-utils/src/ckeditorerror.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@ export default class CKEditorError extends Error {
* data object will also be later available under the {@link #data} property.
*/
constructor( errorName, context, data ) {
const message = `${ errorName }${ ( data ? ` ${ JSON.stringify( data ) }` : '' ) }${ getLinkToDocumentationMessage( errorName ) }`;

super( message );
super( getErrorMessage( errorName, data ) );

/**
* @type {String}
Expand Down Expand Up @@ -172,10 +170,47 @@ export function logError( errorName, data ) {
console.error( ...formatConsoleArguments( errorName, data ) );
}

// Returns formatted link to documentation message.
//
// @private
// @param {String} errorName
// @returns {string}
function getLinkToDocumentationMessage( errorName ) {
return `\nRead more: ${ DOCUMENTATION_URL }#error-${ errorName }`;
}

// Returns formatted error message.
//
// @private
// @param {String} errorName
// @param {Object} [data]
// @returns {string}
function getErrorMessage( errorName, data ) {
const processedObjects = new WeakSet();
const circularReferencesReplacer = ( key, value ) => {
if ( typeof value === 'object' && value !== null ) {
if ( processedObjects.has( value ) ) {
return `[object ${ value.constructor.name }]`;
}

processedObjects.add( value );
}

return value;
};

const stringifiedData = data ? ` ${ JSON.stringify( data, circularReferencesReplacer ) }` : '';
const documentationLink = getLinkToDocumentationMessage( errorName );

return errorName + stringifiedData + documentationLink;
}

// Returns formatted console error arguments.
//
// @private
// @param {String} errorName
// @param {Object} [data]
// @returns {Array}
function formatConsoleArguments( errorName, data ) {
const documentationMessage = getLinkToDocumentationMessage( errorName );

Expand Down
28 changes: 28 additions & 0 deletions packages/ckeditor5-utils/tests/ckeditorerror.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,34 @@ describe( 'CKEditorError', () => {
expect( error ).to.have.property( 'data', data );
} );

it( 'appends stringified data to the message if stringified object contains circular references', () => {
const data = { foo: 'bar' };

data.bar = data;

const error = new CKEditorError( 'foo', null, data );

expect( error ).to.have.property(
'message',
`foo {"foo":"bar","bar":"[object Object]"}\nRead more: ${ DOCUMENTATION_URL }#error-foo`
);
expect( error ).to.have.property( 'data', data );
} );

it( 'appends stringified data to the message if stringified object contains multiple exact same circular references', () => {
const data = { foo: 'bar' };

data.bar = [ data, data ];

const error = new CKEditorError( 'foo', null, data );

expect( error ).to.have.property(
'message',
`foo {"foo":"bar","bar":["[object Object]","[object Object]"]}\nRead more: ${ DOCUMENTATION_URL }#error-foo`
);
expect( error ).to.have.property( 'data', data );
} );

it( 'contains a link which leads to the documentation', () => {
const error = new CKEditorError( 'model-schema-no-item', null );

Expand Down

0 comments on commit 6741143

Please sign in to comment.