Skip to content

Commit

Permalink
Raw handling: fix block schema merging (#56558)
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix authored Nov 28, 2023
1 parent c6be294 commit ece0838
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 47 deletions.
2 changes: 0 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion packages/blocks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
"@wordpress/shortcode": "file:../shortcode",
"change-case": "^4.1.2",
"colord": "^2.7.0",
"deepmerge": "^4.3.0",
"fast-deep-equal": "^3.1.3",
"hpq": "^1.3.0",
"is-plain-object": "^5.0.0",
Expand Down
94 changes: 50 additions & 44 deletions packages/blocks/src/api/raw-handling/utils.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/**
* External dependencies
*/
import deepmerge from 'deepmerge';

/**
* WordPress dependencies
*/
Expand All @@ -14,41 +9,6 @@ import { isPhrasingContent, getPhrasingContentSchema } from '@wordpress/dom';
import { hasBlockSupport } from '..';
import { getRawTransforms } from './get-raw-transforms';

const customMerge = ( key ) => {
return ( srcValue, objValue ) => {
switch ( key ) {
case 'children': {
if ( objValue === '*' || srcValue === '*' ) {
return '*';
}

return { ...objValue, ...srcValue };
}
case 'attributes':
case 'require': {
return [ ...( objValue || [] ), ...( srcValue || [] ) ];
}
case 'isMatch': {
// If one of the values being merge is undefined (matches everything),
// the result of the merge will be undefined.
if ( ! objValue || ! srcValue ) {
return undefined;
}
// When merging two isMatch functions, the result is a new function
// that returns if one of the source functions returns true.
return ( ...args ) => {
return objValue( ...args ) || srcValue( ...args );
};
}
}

return deepmerge( objValue, srcValue, {
customMerge,
clone: false,
} );
};
};

export function getBlockContentSchemaFromTransforms( transforms, context ) {
const phrasingContentSchema = getPhrasingContentSchema( context );
const schemaArgs = { phrasingContentSchema, isPaste: context === 'paste' };
Expand Down Expand Up @@ -86,10 +46,56 @@ export function getBlockContentSchemaFromTransforms( transforms, context ) {
);
} );

return deepmerge.all( schemas, {
customMerge,
clone: false,
} );
function mergeTagNameSchemaProperties( objValue, srcValue, key ) {
switch ( key ) {
case 'children': {
if ( objValue === '*' || srcValue === '*' ) {
return '*';
}

return { ...objValue, ...srcValue };
}
case 'attributes':
case 'require': {
return [ ...( objValue || [] ), ...( srcValue || [] ) ];
}
case 'isMatch': {
// If one of the values being merge is undefined (matches everything),
// the result of the merge will be undefined.
if ( ! objValue || ! srcValue ) {
return undefined;
}
// When merging two isMatch functions, the result is a new function
// that returns if one of the source functions returns true.
return ( ...args ) => {
return objValue( ...args ) || srcValue( ...args );
};
}
}
}

// A tagName schema is an object with children, attributes, require, and
// isMatch properties.
function mergeTagNameSchemas( a, b ) {
for ( const key in b ) {
a[ key ] = a[ key ]
? mergeTagNameSchemaProperties( a[ key ], b[ key ], key )
: { ...b[ key ] };
}
return a;
}

// A schema is an object with tagName schemas by tag name.
function mergeSchemas( a, b ) {
for ( const key in b ) {
a[ key ] = a[ key ]
? mergeTagNameSchemas( a[ key ], b[ key ] )
: { ...b[ key ] };
}
return a;
}

return schemas.reduce( mergeSchemas, {} );
}

/**
Expand Down
28 changes: 28 additions & 0 deletions test/integration/blocks-raw-handling.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,34 @@ describe( 'Blocks raw handling', () => {
expect( console ).toHaveLogged();
} );

it( 'should convert pre', () => {
const transformed = pasteHandler( {
HTML: '<pre>1\n2</pre>',
plainText: '1\n2',
} )
.map( getBlockContent )
.join( '' );

expect( transformed ).toBe(
'<pre class="wp-block-preformatted">1\n2</pre>'
);
expect( console ).toHaveLogged();
} );

it( 'should convert code', () => {
const transformed = pasteHandler( {
HTML: '<pre><code>1\n2</code></pre>',
plainText: '1\n2',
} )
.map( getBlockContent )
.join( '' );

expect( transformed ).toBe(
'<pre class="wp-block-code"><code>1\n2</code></pre>'
);
expect( console ).toHaveLogged();
} );

describe( 'pasteHandler', () => {
[
'plain',
Expand Down

0 comments on commit ece0838

Please sign in to comment.