From ad47a205c3fcff04cb6b42dff580f567bdc0734f Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Wed, 8 May 2024 10:56:45 +0100 Subject: [PATCH 1/9] Convert blocks that are an alias block into their canonical block --- .../src/api/parser/convert-alias-block.js | 21 ++++++++++++++ packages/blocks/src/api/parser/index.js | 29 +++++++++++++++++++ packages/blocks/src/api/serializer.js | 14 +++++---- 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 packages/blocks/src/api/parser/convert-alias-block.js 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 0000000000000..0f58649842da8 --- /dev/null +++ b/packages/blocks/src/api/parser/convert-alias-block.js @@ -0,0 +1,21 @@ +/** + * Convert alias blocks to their canonical form. This function is used + * both in the parser level for previous content and to convert such blocks + * used in Custom Post Types templates. + * + * @param {string} name The block's name + * @param {Object} attributes The block's attributes + * + * @return {[string, Object]} The block's name and attributes, changed accordingly if a match was found + */ +export function convertAliasBlockNameAndAttributes( name, attributes ) { + const canonicalBlockName = attributes.metadata?.alias; + let blockName = name; + const newAttributes = { ...attributes }; + if ( canonicalBlockName ) { + blockName = canonicalBlockName; + newAttributes.metadata.alias = name; + } + + return [ blockName, newAttributes ]; +} diff --git a/packages/blocks/src/api/parser/index.js b/packages/blocks/src/api/parser/index.js index f8ff0c68964dc..d5b1896e8b7ea 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 { convertAliasBlockNameAndAttributes } 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,31 @@ function convertLegacyBlocks( rawBlock ) { }; } +/** + * Convert alias blocks to their canonical form. This function is used + * both in the parser level for previous content and to convert such blocks + * used in Custom Post Types templates. + * + * We are swapping the alias value with the block name depending on whether we are serializing or parsing. + * This is because the alias is used to serialize the block name, but when parsing, we need to convert the alias to the block name. + * + * @param {WPRawBlock} rawBlock + * + * @return {WPRawBlock} The block's name and attributes, changed accordingly if a match was found + */ +function convertAliasBlocks( rawBlock ) { + const [ correctName, correctedAttributes ] = + convertAliasBlockNameAndAttributes( + rawBlock.blockName, + rawBlock.attrs + ); + return { + ...rawBlock, + blockName: correctName, + attrs: correctedAttributes, + }; +} + /** * Normalize the raw block by applying the fallback block name if none given, * sanitize the parsed HTML... @@ -201,6 +227,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/serializer.js b/packages/blocks/src/api/serializer.js index 609e62fc7e84b..c2751f79e83e7 100644 --- a/packages/blocks/src/api/serializer.js +++ b/packages/blocks/src/api/serializer.js @@ -21,6 +21,7 @@ import { } from './registration'; import { serializeRawBlock } from './parser/serialize-raw-block'; import { isUnmodifiedDefaultBlock, normalizeBlockType } from './utils'; +import { convertAliasBlockNameAndAttributes } from './parser/convert-alias-block'; /** @typedef {import('./parser').WPBlock} WPBlock */ @@ -322,15 +323,18 @@ export function getCommentDelimitedContent( attributes, content ) { + const [ correctBlockName, correctedAttributes ] = + convertAliasBlockNameAndAttributes( rawBlockName, attributes ); + const serializedAttributes = - attributes && Object.entries( attributes ).length - ? serializeAttributes( attributes ) + ' ' + correctedAttributes && Object.entries( correctedAttributes ).length + ? serializeAttributes( correctedAttributes ) + ' ' : ''; // Strip core blocks of their namespace prefix. - const blockName = rawBlockName?.startsWith( 'core/' ) - ? rawBlockName.slice( 5 ) - : rawBlockName; + const blockName = correctBlockName?.startsWith( 'core/' ) + ? correctBlockName.slice( 5 ) + : correctBlockName; // @todo make the `wp:` prefix potentially configurable. From d85aa0dd479c6f5c630bce32c200406dcb029247 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Wed, 15 May 2024 14:04:34 +0200 Subject: [PATCH 2/9] Block parser: Allow slash in block name --- packages/block-serialization-default-parser/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-serialization-default-parser/src/index.js b/packages/block-serialization-default-parser/src/index.js index 9fd67cde2b1d9..3c722aeee6294 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. From e646c5c0cce294412c9351150d3b35698a328dc6 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Wed, 15 May 2024 14:52:06 +0200 Subject: [PATCH 3/9] Extract canonical blockName, store in metadata.canonical --- .../src/api/parser/convert-alias-block.js | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/blocks/src/api/parser/convert-alias-block.js b/packages/blocks/src/api/parser/convert-alias-block.js index 0f58649842da8..9885dd5a0ed22 100644 --- a/packages/blocks/src/api/parser/convert-alias-block.js +++ b/packages/blocks/src/api/parser/convert-alias-block.js @@ -1,3 +1,8 @@ +/** + * Internal dependencies + */ +import { getBlockVariations, getBlockType } from '../registration'; + /** * Convert alias blocks to their canonical form. This function is used * both in the parser level for previous content and to convert such blocks @@ -9,13 +14,27 @@ * @return {[string, Object]} The block's name and attributes, changed accordingly if a match was found */ export function convertAliasBlockNameAndAttributes( name, attributes ) { - const canonicalBlockName = attributes.metadata?.alias; - let blockName = name; - const newAttributes = { ...attributes }; + let canonicalBlockName = name; + + const blockVariation = name.split( '/' )?.[ 2 ]; + if ( blockVariation ) { + // FIXME: Stabler way of extracting canonical block name. + canonicalBlockName = name.replace( `/${ blockVariation }`, '' ); + // const variations = getBlockVariations( canonicalBlockName ); + // TODO: Validate. (Check if variation exists in variations array.) + } + + const { metadata = {}, ...otherAttributes } = attributes; + + let newAttributes = { ...otherAttributes }; + + // TODO: Tidy up a bit more. Replace with attribute inference (in serializer)? if ( canonicalBlockName ) { - blockName = canonicalBlockName; - newAttributes.metadata.alias = name; + newAttributes = { + ...otherAttributes, + metadata: { ...metadata, canonical: canonicalBlockName }, + }; } - return [ blockName, newAttributes ]; + return [ canonicalBlockName, newAttributes ]; } From b740b982128c03228c5d007f88c18a2325701501 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Wed, 15 May 2024 16:36:47 +0200 Subject: [PATCH 4/9] Store variation in metadata (instead of canonical block name --- packages/blocks/src/api/parser/convert-alias-block.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/blocks/src/api/parser/convert-alias-block.js b/packages/blocks/src/api/parser/convert-alias-block.js index 9885dd5a0ed22..30234b2279d0e 100644 --- a/packages/blocks/src/api/parser/convert-alias-block.js +++ b/packages/blocks/src/api/parser/convert-alias-block.js @@ -29,10 +29,10 @@ export function convertAliasBlockNameAndAttributes( name, attributes ) { let newAttributes = { ...otherAttributes }; // TODO: Tidy up a bit more. Replace with attribute inference (in serializer)? - if ( canonicalBlockName ) { + if ( blockVariation ) { newAttributes = { ...otherAttributes, - metadata: { ...metadata, canonical: canonicalBlockName }, + metadata: { ...metadata, variation: blockVariation }, }; } From 957b0064d42fb6452d13e40940f4c0a35cec03d2 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Wed, 15 May 2024 16:37:40 +0200 Subject: [PATCH 5/9] Persist block variation when serializing --- packages/blocks/src/api/serializer.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/blocks/src/api/serializer.js b/packages/blocks/src/api/serializer.js index c2751f79e83e7..15480ed57a965 100644 --- a/packages/blocks/src/api/serializer.js +++ b/packages/blocks/src/api/serializer.js @@ -323,8 +323,16 @@ export function getCommentDelimitedContent( attributes, content ) { - const [ correctBlockName, correctedAttributes ] = - convertAliasBlockNameAndAttributes( rawBlockName, attributes ); + const variation = attributes?.metadata?.variation; + + const correctBlockName = variation + ? `${ rawBlockName }/${ variation }` + : rawBlockName; + const correctedAttributes = { ...attributes }; + + if ( variation ) { + delete correctedAttributes.metadata.variation; + } const serializedAttributes = correctedAttributes && Object.entries( correctedAttributes ).length From a3a10b162df21a3c37d34b9013627ed1803621e9 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Thu, 23 May 2024 11:10:33 +0200 Subject: [PATCH 6/9] Infer block variation --- .../src/api/parser/convert-alias-block.js | 15 +------------ packages/blocks/src/api/registration.js | 8 +++++++ packages/blocks/src/api/serializer.js | 22 ++++++++----------- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/packages/blocks/src/api/parser/convert-alias-block.js b/packages/blocks/src/api/parser/convert-alias-block.js index 30234b2279d0e..1918aedcdaf08 100644 --- a/packages/blocks/src/api/parser/convert-alias-block.js +++ b/packages/blocks/src/api/parser/convert-alias-block.js @@ -23,18 +23,5 @@ export function convertAliasBlockNameAndAttributes( name, attributes ) { // const variations = getBlockVariations( canonicalBlockName ); // TODO: Validate. (Check if variation exists in variations array.) } - - const { metadata = {}, ...otherAttributes } = attributes; - - let newAttributes = { ...otherAttributes }; - - // TODO: Tidy up a bit more. Replace with attribute inference (in serializer)? - if ( blockVariation ) { - newAttributes = { - ...otherAttributes, - metadata: { ...metadata, variation: blockVariation }, - }; - } - - return [ canonicalBlockName, newAttributes ]; + return [ canonicalBlockName, attributes ]; } diff --git a/packages/blocks/src/api/registration.js b/packages/blocks/src/api/registration.js index fb21b7083b0c5..36428621029e4 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 15480ed57a965..7c34d8f2d6d6a 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, @@ -323,26 +324,21 @@ export function getCommentDelimitedContent( attributes, content ) { - const variation = attributes?.metadata?.variation; + const variation = getActiveBlockVariation( rawBlockName, attributes ); - const correctBlockName = variation - ? `${ rawBlockName }/${ variation }` + const blockName = variation + ? `${ rawBlockName }/${ variation.name }` : rawBlockName; - const correctedAttributes = { ...attributes }; - - if ( variation ) { - delete correctedAttributes.metadata.variation; - } const serializedAttributes = - correctedAttributes && Object.entries( correctedAttributes ).length - ? serializeAttributes( correctedAttributes ) + ' ' + attributes && Object.entries( attributes ).length + ? serializeAttributes( attributes ) + ' ' : ''; // Strip core blocks of their namespace prefix. - const blockName = correctBlockName?.startsWith( 'core/' ) - ? correctBlockName.slice( 5 ) - : correctBlockName; + // blockName = blockName?.startsWith( 'core/' ) + // ? blockName.slice( 5 ) + // : blockName; // @todo make the `wp:` prefix potentially configurable. From 073e89376032e544c053314ed22aede88ea16473 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Thu, 23 May 2024 11:13:10 +0200 Subject: [PATCH 7/9] Remove obsolete import --- packages/blocks/src/api/serializer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/blocks/src/api/serializer.js b/packages/blocks/src/api/serializer.js index 7c34d8f2d6d6a..ce66d8b11ced8 100644 --- a/packages/blocks/src/api/serializer.js +++ b/packages/blocks/src/api/serializer.js @@ -22,7 +22,6 @@ import { } from './registration'; import { serializeRawBlock } from './parser/serialize-raw-block'; import { isUnmodifiedDefaultBlock, normalizeBlockType } from './utils'; -import { convertAliasBlockNameAndAttributes } from './parser/convert-alias-block'; /** @typedef {import('./parser').WPBlock} WPBlock */ From ab3d6e8ebb14a645be8665843e752880d1fdeef8 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Thu, 23 May 2024 11:20:47 +0200 Subject: [PATCH 8/9] Simplify --- .../src/api/parser/convert-alias-block.js | 17 ++++++----------- packages/blocks/src/api/parser/index.js | 17 +++++------------ 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/packages/blocks/src/api/parser/convert-alias-block.js b/packages/blocks/src/api/parser/convert-alias-block.js index 1918aedcdaf08..cf254b2c3fbdc 100644 --- a/packages/blocks/src/api/parser/convert-alias-block.js +++ b/packages/blocks/src/api/parser/convert-alias-block.js @@ -5,23 +5,18 @@ import { getBlockVariations, getBlockType } from '../registration'; /** * Convert alias blocks to their canonical form. This function is used - * both in the parser level for previous content and to convert such blocks - * used in Custom Post Types templates. + * at the parser level for previous content. * - * @param {string} name The block's name - * @param {Object} attributes The block's attributes + * @param {string} name The block's name, possibly with a variation suffix. * - * @return {[string, Object]} The block's name and attributes, changed accordingly if a match was found + * @return {string} The block's canonical name, with the variation suffix removed. */ -export function convertAliasBlockNameAndAttributes( name, attributes ) { - let canonicalBlockName = name; - +export function stripBlockVariationSuffixFromBlockName( name ) { const blockVariation = name.split( '/' )?.[ 2 ]; if ( blockVariation ) { - // FIXME: Stabler way of extracting canonical block name. - canonicalBlockName = name.replace( `/${ blockVariation }`, '' ); + return name.replace( `/${ blockVariation }`, '' ); // const variations = getBlockVariations( canonicalBlockName ); // TODO: Validate. (Check if variation exists in variations array.) } - return [ canonicalBlockName, attributes ]; + return name; } diff --git a/packages/blocks/src/api/parser/index.js b/packages/blocks/src/api/parser/index.js index d5b1896e8b7ea..392f6b293879d 100644 --- a/packages/blocks/src/api/parser/index.js +++ b/packages/blocks/src/api/parser/index.js @@ -16,7 +16,7 @@ import { getSaveContent } from '../serializer'; import { validateBlock } from '../validation'; import { createBlock } from '../factory'; import { convertLegacyBlockNameAndAttributes } from './convert-legacy-block'; -import { convertAliasBlockNameAndAttributes } from './convert-alias-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'; @@ -81,26 +81,19 @@ function convertLegacyBlocks( rawBlock ) { /** * Convert alias blocks to their canonical form. This function is used - * both in the parser level for previous content and to convert such blocks - * used in Custom Post Types templates. - * - * We are swapping the alias value with the block name depending on whether we are serializing or parsing. - * This is because the alias is used to serialize the block name, but when parsing, we need to convert the alias to the block name. + * 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, correctedAttributes ] = - convertAliasBlockNameAndAttributes( - rawBlock.blockName, - rawBlock.attrs - ); + const correctName = stripBlockVariationSuffixFromBlockName( + rawBlock.blockName + ); return { ...rawBlock, blockName: correctName, - attrs: correctedAttributes, }; } From 37c734611a61a83962ae11bc057b99d4fdd025a3 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Thu, 23 May 2024 11:44:32 +0200 Subject: [PATCH 9/9] Allow unprefixed Core block variations --- .../src/api/parser/convert-alias-block.js | 31 ++++++++++++++----- packages/blocks/src/api/serializer.js | 8 ++--- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/packages/blocks/src/api/parser/convert-alias-block.js b/packages/blocks/src/api/parser/convert-alias-block.js index cf254b2c3fbdc..45f67c7f00aae 100644 --- a/packages/blocks/src/api/parser/convert-alias-block.js +++ b/packages/blocks/src/api/parser/convert-alias-block.js @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import { getBlockVariations, getBlockType } from '../registration'; +import { getBlockVariations } from '../registration'; /** * Convert alias blocks to their canonical form. This function is used @@ -12,11 +12,28 @@ import { getBlockVariations, getBlockType } from '../registration'; * @return {string} The block's canonical name, with the variation suffix removed. */ export function stripBlockVariationSuffixFromBlockName( name ) { - const blockVariation = name.split( '/' )?.[ 2 ]; - if ( blockVariation ) { - return name.replace( `/${ blockVariation }`, '' ); - // const variations = getBlockVariations( canonicalBlockName ); - // TODO: Validate. (Check if variation exists in variations array.) + 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 ]; } - return name; + + // 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/serializer.js b/packages/blocks/src/api/serializer.js index ce66d8b11ced8..781a5a9c8fd50 100644 --- a/packages/blocks/src/api/serializer.js +++ b/packages/blocks/src/api/serializer.js @@ -325,7 +325,7 @@ export function getCommentDelimitedContent( ) { const variation = getActiveBlockVariation( rawBlockName, attributes ); - const blockName = variation + let blockName = variation ? `${ rawBlockName }/${ variation.name }` : rawBlockName; @@ -335,9 +335,9 @@ export function getCommentDelimitedContent( : ''; // Strip core blocks of their namespace prefix. - // blockName = blockName?.startsWith( 'core/' ) - // ? blockName.slice( 5 ) - // : blockName; + blockName = blockName?.startsWith( 'core/' ) + ? blockName.slice( 5 ) + : blockName; // @todo make the `wp:` prefix potentially configurable.