@wordpress/blocks
Version:
Block API for WordPress.
117 lines (101 loc) • 3.24 kB
JavaScript
/**
* External dependencies
*/
import { some, castArray, find, mapValues, pickBy, includes } from 'lodash';
/**
* WordPress dependencies
*/
import { regexp, next } from '@wordpress/shortcode';
/**
* Internal dependencies
*/
import { createBlock, getBlockTransforms, findTransform } from '../factory';
import { getBlockType } from '../registration';
import { getBlockAttributes } from '../parser';
function segmentHTMLToShortcodeBlock(
HTML,
lastIndex = 0,
excludedBlockNames = []
) {
// Get all matches.
const transformsFrom = getBlockTransforms( 'from' );
const transformation = findTransform(
transformsFrom,
( transform ) =>
excludedBlockNames.indexOf( transform.blockName ) === -1 &&
transform.type === 'shortcode' &&
some( castArray( transform.tag ), ( tag ) =>
regexp( tag ).test( HTML )
)
);
if ( ! transformation ) {
return [ HTML ];
}
const transformTags = castArray( transformation.tag );
const transformTag = find( transformTags, ( tag ) =>
regexp( tag ).test( HTML )
);
let match;
const previousIndex = lastIndex;
if ( ( match = next( transformTag, HTML, lastIndex ) ) ) {
lastIndex = match.index + match.content.length;
const beforeHTML = HTML.substr( 0, match.index );
const afterHTML = HTML.substr( lastIndex );
// If the shortcode content does not contain HTML and the shortcode is
// not on a new line (or in paragraph from Markdown converter),
// consider the shortcode as inline text, and thus skip conversion for
// this segment.
if (
! includes( match.shortcode.content || '', '<' ) &&
! (
/(\n|<p>)\s*$/.test( beforeHTML ) &&
/^\s*(\n|<\/p>)/.test( afterHTML )
)
) {
return segmentHTMLToShortcodeBlock( HTML, lastIndex );
}
// If a transformation's `isMatch` predicate fails for the inbound
// shortcode, try again by excluding the current block type.
//
// This is the only call to `segmentHTMLToShortcodeBlock` that should
// ever carry over `excludedBlockNames`. Other calls in the module
// should skip that argument as a way to reset the exclusion state, so
// that one `isMatch` fail in an HTML fragment doesn't prevent any
// valid matches in subsequent fragments.
if (
transformation.isMatch &&
! transformation.isMatch( match.shortcode.attrs )
) {
return segmentHTMLToShortcodeBlock( HTML, previousIndex, [
...excludedBlockNames,
transformation.blockName,
] );
}
const attributes = mapValues(
pickBy( transformation.attributes, ( schema ) => schema.shortcode ),
// Passing all of `match` as second argument is intentionally broad
// but shouldn't be too relied upon.
//
// See: https://github.com/WordPress/gutenberg/pull/3610#discussion_r152546926
( schema ) => schema.shortcode( match.shortcode.attrs, match )
);
const block = createBlock(
transformation.blockName,
getBlockAttributes(
{
...getBlockType( transformation.blockName ),
attributes: transformation.attributes,
},
match.shortcode.content,
attributes
)
);
return [
...segmentHTMLToShortcodeBlock( beforeHTML ),
block,
...segmentHTMLToShortcodeBlock( afterHTML ),
];
}
return [ HTML ];
}
export default segmentHTMLToShortcodeBlock;