Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block Variations: Include variation name in serialized block name #61678

Draft
wants to merge 9 commits into
base: trunk
Choose a base branch
from
2 changes: 1 addition & 1 deletion packages/block-serialization-default-parser/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ let stack;
* @since 4.6.1 added optimization to prevent backtracking on attribute parsing
*/
const tokenizer =
/<!--\s+(\/)?wp:([a-z][a-z0-9_-]*\/)?([a-z][a-z0-9_-]*)\s+({(?:(?=([^}]+|}+(?=})|(?!}\s+\/?-->)[^])*)\5|[^]*?)}\s+)?(\/)?-->/g;
/<!--\s+(\/)?wp:([a-z][a-z0-9_-]*\/)?([a-z][a-z0-9/_-]*)\s+({(?:(?=([^}]+|}+(?=})|(?!}\s+\/?-->)[^])*)\5|[^]*?)}\s+)?(\/)?-->/g;

/**
* Constructs a block object.
Expand Down
39 changes: 39 additions & 0 deletions packages/blocks/src/api/parser/convert-alias-block.js
Original file line number Diff line number Diff line change
@@ -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 ];
}
22 changes: 22 additions & 0 deletions packages/blocks/src/api/parser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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...
Expand Down Expand Up @@ -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 );

Expand Down
8 changes: 8 additions & 0 deletions packages/blocks/src/api/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
13 changes: 10 additions & 3 deletions packages/blocks/src/api/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { removep } from '@wordpress/autop';
* Internal dependencies
*/
import {
getActiveBlockVariation,
getBlockType,
getFreeformContentHandlerName,
getUnregisteredTypeHandlerName,
Expand Down Expand Up @@ -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.

Expand Down