From 4b075770bad663daee05895e01d47f39d2bc98b9 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Wed, 26 Jun 2019 15:23:24 -0400 Subject: [PATCH 1/2] Block API: Add parse options to specify custom source handling --- packages/blocks/README.md | 1 + packages/blocks/src/api/parser.js | 78 ++++++++++++++++++++++--------- 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/packages/blocks/README.md b/packages/blocks/README.md index cf83bc0e3b0870..71ff5d75d3fbc0 100644 --- a/packages/blocks/README.md +++ b/packages/blocks/README.md @@ -244,6 +244,7 @@ _Parameters_ - _blockTypeOrName_ `(string|Object)`: Block type or name. - _innerHTML_ `string`: Raw block content. - _attributes_ `?Object`: Known block attributes (from delimiters). +- _parseOptions_ `WPBlocksParseOptions`: Parser options. _Returns_ diff --git a/packages/blocks/src/api/parser.js b/packages/blocks/src/api/parser.js index 40c752c8305c6d..09dda5202ac70e 100644 --- a/packages/blocks/src/api/parser.js +++ b/packages/blocks/src/api/parser.js @@ -2,7 +2,7 @@ * External dependencies */ import { parse as hpqParse } from 'hpq'; -import { flow, castArray, mapValues, omit, stubFalse } from 'lodash'; +import { flow, castArray, mapValues, omit, stubFalse, has } from 'lodash'; /** * WordPress dependencies @@ -25,6 +25,26 @@ import { getCommentDelimitedContent } from './serializer'; import { attr, html, text, query, node, children, prop } from './matchers'; import { normalizeBlockType } from './utils'; +/** + * Options for parse. + * + * @property {Object} sources An object defining custom source + * types for a parse, where the key + * corresponds to the source name, + * the value a function receiving + * an attribute schema and expected + * to return the desired value. + * + * @typedef {Object} WPBlocksParseOptions + */ + +/** + * Default options for parse. + * + * @type {WPBlocksParseOptions} + */ +const DEFAULT_PARSE_OPTIONS = {}; + /** * Sources which are guaranteed to return a string value. * @@ -225,14 +245,15 @@ export function parseWithAttributeSchema( innerHTML, attributeSchema ) { * commentAttributes returns the attribute value depending on its source * definition of the given attribute key. * - * @param {string} attributeKey Attribute key. - * @param {Object} attributeSchema Attribute's schema. - * @param {string} innerHTML Block's raw content. - * @param {Object} commentAttributes Block's comment attributes. + * @param {string} attributeKey Attribute key. + * @param {Object} attributeSchema Attribute's schema. + * @param {string} innerHTML Block's raw content. + * @param {Object} commentAttributes Block's comment attributes. + * @param {WPBlocksParseOptions} parseOptions Parser options. * * @return {*} Attribute value. */ -export function getBlockAttribute( attributeKey, attributeSchema, innerHTML, commentAttributes ) { +export function getBlockAttribute( attributeKey, attributeSchema, innerHTML, commentAttributes, parseOptions ) { const { type, enum: enumSet } = attributeSchema; let value; @@ -241,6 +262,7 @@ export function getBlockAttribute( attributeKey, attributeSchema, innerHTML, com case undefined: value = commentAttributes ? commentAttributes[ attributeKey ] : undefined; break; + case 'attribute': case 'property': case 'html': @@ -251,6 +273,11 @@ export function getBlockAttribute( attributeKey, attributeSchema, innerHTML, com case 'tag': value = parseWithAttributeSchema( innerHTML, attributeSchema ); break; + + default: + if ( has( parseOptions.sources, attributeSchema.source ) ) { + value = parseOptions.sources[ attributeSchema.source ]( attributeSchema ); + } } if ( ! isValidByType( value, type ) || ! isValidByEnum( value, enumSet ) ) { @@ -269,16 +296,18 @@ export function getBlockAttribute( attributeKey, attributeSchema, innerHTML, com /** * Returns the block attributes of a registered block node given its type. * - * @param {string|Object} blockTypeOrName Block type or name. - * @param {string} innerHTML Raw block content. - * @param {?Object} attributes Known block attributes (from delimiters). + * @param {string|Object} blockTypeOrName Block type or name. + * @param {string} innerHTML Raw block content. + * @param {?Object} attributes Known block attributes (from + * delimiters). + * @param {WPBlocksParseOptions} parseOptions Parser options. * * @return {Object} All block attributes. */ -export function getBlockAttributes( blockTypeOrName, innerHTML, attributes = {} ) { +export function getBlockAttributes( blockTypeOrName, innerHTML, attributes = {}, parseOptions ) { const blockType = normalizeBlockType( blockTypeOrName ); const blockAttributes = mapValues( blockType.attributes, ( attributeSchema, attributeKey ) => { - return getBlockAttribute( attributeKey, attributeSchema, innerHTML, attributes ); + return getBlockAttribute( attributeKey, attributeSchema, innerHTML, attributes, parseOptions ); } ); return applyFilters( @@ -372,11 +401,12 @@ export function getMigratedBlock( block, parsedAttributes ) { /** * Creates a block with fallback to the unknown type handler. * - * @param {Object} blockNode Parsed block node. + * @param {Object} blockNode Parsed block node. + * @param {WPBlocksParseOptions} parseOptions Parser options. * * @return {?Object} An initialized block object (if possible). */ -export function createBlockWithFallback( blockNode ) { +export function createBlockWithFallback( blockNode, parseOptions ) { const { blockName: originalName } = blockNode; let { attrs: attributes, @@ -445,7 +475,7 @@ export function createBlockWithFallback( blockNode ) { let block = createBlock( name, - getBlockAttributes( blockType, innerHTML, attributes ), + getBlockAttributes( blockType, innerHTML, attributes, parseOptions ), innerBlocks ); @@ -473,14 +503,18 @@ export function createBlockWithFallback( blockNode ) { * * @return {Function} An implementation which parses the post content. */ -const createParse = ( parseImplementation ) => - ( content ) => parseImplementation( content ).reduce( ( memo, blockNode ) => { - const block = createBlockWithFallback( blockNode ); - if ( block ) { - memo.push( block ); - } - return memo; - }, [] ); +function createParse( parseImplementation ) { + return ( content, options = DEFAULT_PARSE_OPTIONS ) => { + return parseImplementation( content ).reduce( ( memo, blockNode ) => { + const block = createBlockWithFallback( blockNode, options ); + if ( block ) { + memo.push( block ); + } + + return memo; + }, [] ); + }; +} /** * Parses the post content with a PegJS grammar and returns a list of blocks. From 63a313daf5d63754f7c1d436bf834373475009a5 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Wed, 26 Jun 2019 15:24:07 -0400 Subject: [PATCH 2/2] Editor: Define custom meta source for initialization parse --- packages/editor/src/store/actions.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 073887663bd1b9..fa21d266bcb32e 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -59,7 +59,11 @@ export function* setupEditor( post, edits, template ) { content = post.content.raw; } - let blocks = parse( content ); + let blocks = parse( content, { + sources: { + meta: ( schema ) => post.meta[ schema.meta ], + }, + } ); // Apply a template for new posts only, if exists. const isNewPost = post.status === 'auto-draft';