Skip to content

Commit

Permalink
Parsing: Declare all block attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed May 8, 2017
1 parent e4d749a commit 1124cfe
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 179 deletions.
4 changes: 1 addition & 3 deletions blocks/api/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/**
* External dependencies
*/
import * as query from './query';

export { query };
export { default as query } from './query';
export { createBlock, switchToBlockType } from './factory';
export { default as parse } from './parser';
export { default as serialize } from './serializer';
Expand Down
51 changes: 14 additions & 37 deletions blocks/api/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* External dependencies
*/
import { parse as hpqParse } from 'hpq';
import { escape, unescape, pickBy } from 'lodash';
import { escape, unescape, reduce } from 'lodash';

/**
* Internal dependencies
Expand All @@ -11,32 +11,6 @@ import { parse as grammarParse } from './post.pegjs';
import { getBlockSettings, getUnknownTypeHandler } from './registration';
import { createBlock } from './factory';

/**
* Returns the block attributes parsed from raw content.
*
* @param {String} rawContent Raw block content
* @param {Object} blockSettings Block settings
* @return {Object} Block attributes
*/
export function parseBlockAttributes( rawContent, blockSettings ) {
const { attributes } = blockSettings;
if ( 'function' === typeof attributes ) {
return attributes( rawContent );
} else if ( attributes ) {
// Matchers are implemented as functions that receive a DOM node from
// which to select data. Use of the DOM is incidental and we shouldn't
// guarantee a contract that this be provided, else block implementers
// may feel compelled to use the node. Instead, matchers are intended
// as a generic interface to query data from any tree shape. Here we
// pick only matchers which include an internal flag.
const knownMatchers = pickBy( attributes, '_wpBlocksKnownMatcher' );

return hpqParse( rawContent, knownMatchers );
}

return {};
}

/**
* Returns the block attributes of a registered block node given its settings.
*
Expand All @@ -46,17 +20,20 @@ export function parseBlockAttributes( rawContent, blockSettings ) {
* @return {Object} All block attributes
*/
export function getBlockAttributes( blockSettings, rawContent, attributes ) {
// Merge any attributes from comment delimiters with block implementation
attributes = attributes || {};
if ( blockSettings ) {
attributes = {
...attributes,
...blockSettings.defaultAttributes,
...parseBlockAttributes( rawContent, blockSettings ),
};
}
const computedAttributes = reduce( blockSettings.attributes, ( memo, attribute, key ) => {
if ( attribute.source === 'metadata' ) {
memo[ key ] = attributes[ attribute.name || key ];
} else if ( attribute.source === 'content' ) {
memo[ key ] = hpqParse( rawContent, attribute.parse );
}

return memo;
}, {} );

return attributes;
return {
...blockSettings.defaultAttributes,
...computedAttributes,
};
}

/**
Expand Down
60 changes: 38 additions & 22 deletions blocks/api/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,16 @@
* External dependencies
*/
import { nodeListToReact } from 'dom-react';
import { flow } from 'lodash';
import {
attr as originalAttr,
prop as originalProp,
html as originalHtml,
text as originalText,
query as originalQuery
} from 'hpq';
import { reduce } from 'lodash';

/**
* Given a matcher function creator, returns a new function which applies an
* internal flag to the created matcher.
*
* @param {Function} fn Original matcher function creator
* @return {Function} Modified matcher function creator
*/
function withKnownMatcherFlag( fn ) {
return flow( fn, ( matcher ) => {
matcher._wpBlocksKnownMatcher = true;
return matcher;
} );
}

export const attr = withKnownMatcherFlag( originalAttr );
export const prop = withKnownMatcherFlag( originalProp );
export const html = withKnownMatcherFlag( originalHtml );
export const text = withKnownMatcherFlag( originalText );
export const query = withKnownMatcherFlag( originalQuery );
export const children = withKnownMatcherFlag( ( selector ) => {
export const originalChildren = ( selector ) => {
return ( node ) => {
let match = node;

Expand All @@ -44,4 +25,39 @@ export const children = withKnownMatcherFlag( ( selector ) => {

return [];
};
} );
};

const addDescriptor = ( description ) => ( memo ) => {
return Object.assign( memo, description );
};

const attr = ( ...args ) => addDescriptor( { source: 'content', parse: originalAttr( ...args ) } );
const prop = ( ...args ) => addDescriptor( { source: 'content', parse: originalProp( ...args ) } );
const html = ( ...args ) => addDescriptor( { source: 'content', parse: originalHtml( ...args ) } );
const text = ( ...args ) => addDescriptor( { source: 'content', parse: originalText( ...args ) } );
const children = ( ...args ) => addDescriptor( { source: 'content', parse: originalChildren( ...args ) } );
const metadata = ( name ) => addDescriptor( { source: 'metadata', name } );
const query = ( selector, descriptor ) => {
return addDescriptor( {
source: 'content',
parse: originalQuery( selector, descriptor.__description.parse )
} );
};

const accumulateOn = ( description ) => {
return reduce( { attr, prop, html, text, query, children, metadata }, ( memo, fct, key ) => {
const wrappedFct = ( ...args ) => {
const accumulator = fct( ...args );
const newDescription = accumulator( description || {} );
return {
...accumulateOn( newDescription ),
__description: newDescription
};
};

memo[ key ] = wrappedFct;
return memo;
}, {} );
};

export default accumulateOn();
13 changes: 12 additions & 1 deletion blocks/api/registration.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* External dependencies
*/
import { reduce } from 'lodash';

/* eslint-disable no-console */

/**
Expand Down Expand Up @@ -43,7 +48,13 @@ export function registerBlock( slug, settings ) {
);
return;
}
const block = Object.assign( { slug }, settings );

const attributes = reduce( settings ? settings.attributes : {}, ( memo, value, key ) => {
memo[ key ] = value.__description;
return memo;
}, {} );

const block = Object.assign( {}, settings, { slug, attributes } );
blocks[ slug ] = block;
return block;
}
Expand Down
33 changes: 12 additions & 21 deletions blocks/api/serializer.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
/**
* External dependencies
*/
import { difference } from 'lodash';
import { reduce } from 'lodash';
import { html as beautifyHtml } from 'js-beautify';

/**
* Internal dependencies
*/
import { getBlockSettings } from './registration';
import { parseBlockAttributes } from './parser';

/**
* Given a block's save render implementation and attributes, returns the
Expand Down Expand Up @@ -37,29 +36,21 @@ export function getSaveContent( save, attributes ) {
}

/**
* Returns comment attributes as serialized string, determined by subset of
* difference between actual attributes of a block and those expected based
* on its settings.
* Returns comment attributes as serialized string
*
* @param {Object} realAttributes Actual block attributes
* @param {Object} expectedAttributes Expected block attributes
* @return {string} Comment attributes
* @param {Object} settings Block settings
* @param {Object} attributes Block attributes
* @return {string} Comment attributes
*/
export function getCommentAttributes( realAttributes, expectedAttributes ) {
// Find difference and build into object subset of attributes.
const keys = difference(
Object.keys( realAttributes ),
Object.keys( expectedAttributes )
);

export function getCommentAttributes( settings, attributes ) {
// Serialize the comment attributes
return keys.reduce( ( memo, key ) => {
const value = realAttributes[ key ];
if ( undefined === value ) {
return memo;
return reduce( settings.attributes, ( memo, attribute, key ) => {
const value = attributes[ key ];
if ( attribute.source === 'metadata' && value !== undefined ) {
return memo + `${ attribute.name || key }="${ value }" `;
}

return memo + `${ key }="${ value }" `;
return memo;
}, '' );
}

Expand All @@ -84,8 +75,8 @@ export default function serialize( blocks ) {
blockType +
' ' +
getCommentAttributes(
settings,
block.attributes,
parseBlockAttributes( saveContent, settings )
) +
'-->' +
( saveContent ? '\n' + beautifyHtml( saveContent, beautifyOptions ) + '\n' : '' ) +
Expand Down
Loading

0 comments on commit 1124cfe

Please sign in to comment.