yoastseo-dep
Version:
Yoast clientside page analysis
139 lines (121 loc) • 4.39 kB
JavaScript
const blockTokenizer = /<!--\s+wp:([a-z][a-z0-9_-]*\/)?([a-z][a-z0-9_-]*)\s+({(?:(?=([^}]+|}+(?=})|(?!}\s+\/?-->)[^])*)\5|[^]*?)}\s+)?(\/)?-->/g;
/**
* Get all the blocks including the inner blocks.
*
* @param {Paper} paper The paper to get all the blocks from.
*
* @returns {Object[]} An array of blocks.
*/
const getAllBlocks = ( paper ) => {
const blocks = paper._attributes.wpBlocks;
const flattenBlocks = [];
if ( ! ( blocks && blocks.length > 0 ) ) {
return [];
}
blocks.forEach( ( block ) => {
if ( block.innerBlocks.length > 0 ) {
const innerBlocks = block.innerBlocks;
flattenBlocks.push(
block, ...innerBlocks
);
} else {
flattenBlocks.push( block );
}
} );
return flattenBlocks;
};
/**
* Gets the offset of each block and set it to the tree.
*
* @param {Object[]} blocks An array of blocks.
* @param {string} text The text.
*
* @returns {void}
*/
function updateBlocksOffset( blocks, text ) {
if ( blocks.length === 0 ) {
return;
}
blocks.forEach( currentBlock => {
const matches = blockTokenizer.exec( text );
if ( null === matches ) {
return;
}
const [ match ] = matches;
const startedAt = matches.index;
const length = match.length;
currentBlock.startOffset = startedAt;
currentBlock.contentOffset = startedAt + length + 1;
if ( currentBlock.innerBlocks && currentBlock.innerBlocks.length > 0 ) {
updateBlocksOffset( currentBlock.innerBlocks, text );
}
} );
}
/**
* Gets the client id for each block and set it to the child nodes.
* Additionally, when a node has an attribute with 'id' key, also set this id to the first.
*
* @param {Node} rootNode The root node.
* @param {Object[]} blocks An array of blocks.
* @param {string} clientId The client id to set.
*
* @returns {void}
*/
function updateClientIdAndAttrIdForSubtree( rootNode, blocks, clientId ) {
if ( ! rootNode ) {
return;
}
let currentClientId = clientId;
if ( rootNode.sourceCodeLocation && rootNode.sourceCodeLocation.startOffset ) {
const foundBlock = blocks.find( block => block.contentOffset === rootNode.sourceCodeLocation.startOffset );
if ( foundBlock ) {
currentClientId = foundBlock.clientId;
}
}
// If the clientId is not undefined, also set this to the root node.
if ( currentClientId ) {
rootNode.clientId = currentClientId;
}
// If the node has children, update the clientId for them.
( rootNode.childNodes || [] ).forEach( ( node ) => {
if ( node.attributes && node.attributes.id ) {
/*
* If the node's child has an attribute with 'id' key, also set this id to the first and third child node.
* This step is specifically for parsing the Yoast blocks.
*
* For Yoast blocks, if a node has attribute with 'id' key, this means that this node represents a sub-block.
* A sub-block has four child nodes:
* 1. The first child node of the sub-block, represents the first section of that sub-block.
* - For example, in Yoast FAQ block, the first child node would represent the "question" section.
* 2. The second child node of the sub-block, only contains a new line.
* 3. The third child node of the sub-block, represents the second section of that sub-block.
* - For example, in Yoast FAQ block, the second child node would represent the "answer" section.
* 4. The fourth child node of the sub-block, only contains a new line.
*/
if ( node.childNodes && node.childNodes.length > 3 ) {
node.childNodes[ 0 ].attributeId = node.attributes.id;
node.childNodes[ 0 ].isFirstSection = true;
node.childNodes[ 2 ].attributeId = node.attributes.id;
node.childNodes[ 2 ].isFirstSection = false;
}
}
updateClientIdAndAttrIdForSubtree( node, blocks, currentClientId );
} );
}
/**
* Parses blocks and updates the clientId for the subtree.
* Additionally, when a node has an attribute with 'id' key, also set this id to the first and third child node of that node.
*
* @param {Paper} paper The paper to parse.
* @param {Node} node The node to parse.
*
* @returns {void}
*/
export default function( paper, node ) {
const blocks = paper._attributes.wpBlocks || [];
blockTokenizer.lastIndex = 0;
updateBlocksOffset( blocks, paper.getText() );
const rawBlocks = getAllBlocks( paper );
// eslint-disable-next-line no-undefined
updateClientIdAndAttrIdForSubtree( node, rawBlocks, undefined );
}