diff --git a/packages/block-serialization-default-parser/src/index.js b/packages/block-serialization-default-parser/src/index.js index 9fd67cde2b1d96..3c722aeee6294e 100644 --- a/packages/block-serialization-default-parser/src/index.js +++ b/packages/block-serialization-default-parser/src/index.js @@ -87,7 +87,7 @@ let stack; * @since 4.6.1 added optimization to prevent backtracking on attribute parsing */ const tokenizer = - /)[^])*)\5|[^]*?)}\s+)?(\/)?-->/g; + /)[^])*)\5|[^]*?)}\s+)?(\/)?-->/g; /** * Constructs a block object. diff --git a/packages/blocks/src/api/parser/convert-alias-block.js b/packages/blocks/src/api/parser/convert-alias-block.js new file mode 100644 index 00000000000000..45f67c7f00aae6 --- /dev/null +++ b/packages/blocks/src/api/parser/convert-alias-block.js @@ -0,0 +1,39 @@ +/** + * Internal dependencies + */ +import { getBlockVariations } from '../registration'; + +/** + * Convert alias blocks to their canonical form. This function is used + * at the parser level for previous content. + * + * @param {string} name The block's name, possibly with a variation suffix. + * + * @return {string} The block's canonical name, with the variation suffix removed. + */ +export function stripBlockVariationSuffixFromBlockName( name ) { + const blockNameParts = name.split( '/' ); + + if ( blockNameParts.length > 2 || blockNameParts[ 0 ] === 'core' ) { + // We're dealing with a block with a variation (e.g. 'my-plugin/my-block/variation'), + // or with a Core block without variation (e.g. 'core/social-link'). + return blockNameParts[ 0 ] + '/' + blockNameParts[ 1 ]; + } else if ( blockNameParts.length === 2 ) { + // We're either dealing with a non-Core block (e.g. 'my-plugin/my-block') + // or with a Core block with a variation (with an implied 'core/' namespace) + // (e.g. 'social-link/wordpress'). + const potentialCoreBlockName = 'core/' + blockNameParts[ 0 ]; + const variations = getBlockVariations( potentialCoreBlockName ); + if ( + variations?.some( + ( variation ) => variation.name === blockNameParts[ 1 ] + ) + ) { + return potentialCoreBlockName; + } + return blockNameParts[ 0 ] + '/' + blockNameParts[ 1 ]; + } + + // We're dealing with a Core block without a variation, and an implied 'core/' namespace. + return blockNameParts[ 0 ]; +} diff --git a/packages/blocks/src/api/parser/index.js b/packages/blocks/src/api/parser/index.js index f8ff0c68964dc3..392f6b293879d6 100644 --- a/packages/blocks/src/api/parser/index.js +++ b/packages/blocks/src/api/parser/index.js @@ -16,6 +16,7 @@ import { getSaveContent } from '../serializer'; import { validateBlock } from '../validation'; import { createBlock } from '../factory'; import { convertLegacyBlockNameAndAttributes } from './convert-legacy-block'; +import { stripBlockVariationSuffixFromBlockName } from './convert-alias-block'; import { serializeRawBlock } from './serialize-raw-block'; import { getBlockAttributes } from './get-block-attributes'; import { applyBlockDeprecatedVersions } from './apply-block-deprecated-versions'; @@ -78,6 +79,24 @@ function convertLegacyBlocks( rawBlock ) { }; } +/** + * Convert alias blocks to their canonical form. This function is used + * at the parser level for previous content. + * + * @param {WPRawBlock} rawBlock + * + * @return {WPRawBlock} The block's name and attributes, changed accordingly if a match was found + */ +function convertAliasBlocks( rawBlock ) { + const correctName = stripBlockVariationSuffixFromBlockName( + rawBlock.blockName + ); + return { + ...rawBlock, + blockName: correctName, + }; +} + /** * Normalize the raw block by applying the fallback block name if none given, * sanitize the parsed HTML... @@ -201,6 +220,9 @@ export function parseRawBlock( rawBlock, options ) { // we added this function to properly parse the old content. normalizedBlock = convertLegacyBlocks( normalizedBlock ); + // Convert alias blocks to their canonical form. + normalizedBlock = convertAliasBlocks( normalizedBlock ); + // Try finding the type for known block name. let blockType = getBlockType( normalizedBlock.blockName ); diff --git a/packages/blocks/src/api/registration.js b/packages/blocks/src/api/registration.js index fb21b7083b0c54..36428621029e40 100644 --- a/packages/blocks/src/api/registration.js +++ b/packages/blocks/src/api/registration.js @@ -690,6 +690,14 @@ export const getBlockVariations = ( blockName, scope ) => { return select( blocksStore ).getBlockVariations( blockName, scope ); }; +export const getActiveBlockVariation = ( blockName, attributes, scope ) => { + return select( blocksStore ).getActiveBlockVariation( + blockName, + attributes, + scope + ); +}; + /** * Registers a new block variation for the given block type. * diff --git a/packages/blocks/src/api/serializer.js b/packages/blocks/src/api/serializer.js index 609e62fc7e84b2..781a5a9c8fd508 100644 --- a/packages/blocks/src/api/serializer.js +++ b/packages/blocks/src/api/serializer.js @@ -15,6 +15,7 @@ import { removep } from '@wordpress/autop'; * Internal dependencies */ import { + getActiveBlockVariation, getBlockType, getFreeformContentHandlerName, getUnregisteredTypeHandlerName, @@ -322,15 +323,21 @@ export function getCommentDelimitedContent( attributes, content ) { + const variation = getActiveBlockVariation( rawBlockName, attributes ); + + let blockName = variation + ? `${ rawBlockName }/${ variation.name }` + : rawBlockName; + const serializedAttributes = attributes && Object.entries( attributes ).length ? serializeAttributes( attributes ) + ' ' : ''; // Strip core blocks of their namespace prefix. - const blockName = rawBlockName?.startsWith( 'core/' ) - ? rawBlockName.slice( 5 ) - : rawBlockName; + blockName = blockName?.startsWith( 'core/' ) + ? blockName.slice( 5 ) + : blockName; // @todo make the `wp:` prefix potentially configurable.