Skip to content

Commit

Permalink
Framework: Extract the block API to a generic blocks-core module with…
Browse files Browse the repository at this point in the history
…out global registration
  • Loading branch information
youknowriad committed Aug 16, 2017
1 parent c8c8078 commit 1f23e41
Show file tree
Hide file tree
Showing 67 changed files with 771 additions and 486 deletions.
2 changes: 1 addition & 1 deletion bin/create-php-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const phpegjs = require( 'phpegjs' );
const fs = require( 'fs' );
const path = require( 'path' );

const peg = fs.readFileSync( 'blocks/api/post.pegjs', 'utf8' );
const peg = fs.readFileSync( 'block-api/post.pegjs', 'utf8' );

const parser = pegjs.generate(
peg,
Expand Down
16 changes: 16 additions & 0 deletions block-api/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* External dependencies
*/
import { find } from 'lodash';

/**
* Retrieves the blockType from the block types config
*
* @param {String} name Block name
* @param {Object} config The block types config
*
* @return {?Object} Block type
*/
export function getBlockType( name, config ) {
return find( config.blockTypes, ( blockType ) => blockType.name === name );
}
28 changes: 16 additions & 12 deletions blocks/api/factory.js → block-api/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,16 @@ import {
/**
* Internal dependencies
*/
import { getBlockType } from './registration';
import { getBlockType } from './config';

/**
* Returns a block object given its type and attributes.
*
* @param {String} name Block name
* @param {String} blockType Block type
* @param {Object} attributes Block attributes
* @return {Object} Block object
*/
export function createBlock( name, attributes = {} ) {
// Get the type definition associated with a registered block.
const blockType = getBlockType( name );

export function createBlock( blockType, attributes = {} ) {
// Ensure attributes contains only values defined by block type, and merge
// default values for missing attributes.
attributes = reduce( blockType.attributes, ( result, source, key ) => {
Expand All @@ -44,7 +41,7 @@ export function createBlock( name, attributes = {} ) {
// and the block attributes.
return {
uid: uuid(),
name,
name: blockType.name,
isValid: true,
attributes,
};
Expand All @@ -54,14 +51,15 @@ export function createBlock( name, attributes = {} ) {
* Switch a block into one or more blocks of the new block type.
*
* @param {Object} block Block object
* @param {string} name Block name
* @param {String} name Block name
* @param {Object} config Block Types config
* @return {Array} Block object
*/
export function switchToBlockType( block, name ) {
export function switchToBlockType( block, name, config ) {
// Find the right transformation by giving priority to the "to"
// transformation.
const destinationType = getBlockType( name );
const sourceType = getBlockType( block.name );
const destinationType = getBlockType( name, config );
const sourceType = getBlockType( block.name, config );
const transformationsFrom = get( destinationType, 'transforms.from', [] );
const transformationsTo = get( sourceType, 'transforms.to', [] );
const transformation =
Expand All @@ -87,10 +85,16 @@ export function switchToBlockType( block, name ) {

// Ensure that every block object returned by the transformation has a
// valid block type.
if ( transformationResults.some( ( result ) => ! getBlockType( result.name ) ) ) {
if ( transformationResults.some( ( result ) => ! getBlockType( result.name, config ) ) ) {
return null;
}

// Pass the results through the createBlockHelper
transformationResults = transformationResults.map( ( result ) => {
const blockType = getBlockType( result.name, config );
return createBlock( blockType, result.attributes );
} );

const firstSwitchedBlock = findIndex( transformationResults, ( result ) => result.name === name );

// Ensure that at least one block object returned by the transformation has
Expand Down
12 changes: 12 additions & 0 deletions block-api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* External dependencies
*/
import * as source from './source';

export { source };
export { createBlock, switchToBlockType } from './factory';
export { default as parse } from './parser';
export { default as pasteHandler } from './paste';
export { default as serialize, getBlockDefaultClassname } from './serializer';
export { parse as grammarParse } from './post.pegjs';
export { getBlockType } from './config';
33 changes: 17 additions & 16 deletions blocks/api/parser.js → block-api/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { mapValues, reduce, pickBy } from 'lodash';
* Internal dependencies
*/
import { parse as grammarParse } from './post.pegjs';
import { getBlockType, getUnknownTypeHandlerName } from './registration';
import { createBlock } from './factory';
import { isValidBlock } from './validation';
import { getBlockType } from './config';

/**
* Returns true if the provided function is a valid attribute source, or false
Expand Down Expand Up @@ -151,14 +151,15 @@ export function getBlockAttributes( blockType, rawContent, attributes ) {
/**
* Creates a block with fallback to the unknown type handler.
*
* @param {?String} name Block type name
* @param {String} rawContent Raw block content
* @param {?Object} attributes Attributes obtained from block delimiters
* @return {?Object} An initialized block object (if possible)
* @param {?String} originalName Block type name
* @param {String} rawContent Raw block content
* @param {?Object} attributes Attributes obtained from block delimiters
* @param {Object} config Block Types config
* @return {?Object} An initialized block object (if possible)
*/
export function createBlockWithFallback( name, rawContent, attributes ) {
export function createBlockWithFallback( originalName, rawContent, attributes, config ) {
// Use type from block content, otherwise find unknown handler.
name = name || getUnknownTypeHandlerName();
let name = originalName || config.fallbackBlockName;

// Convert 'core/text' blocks in existing content to the new
// 'core/paragraph'.
Expand All @@ -167,21 +168,20 @@ export function createBlockWithFallback( name, rawContent, attributes ) {
}

// Try finding type for known block name, else fall back again.
let blockType = getBlockType( name );
const fallbackBlock = getUnknownTypeHandlerName();
let blockType = getBlockType( name, config );
if ( ! blockType ) {
name = fallbackBlock;
blockType = getBlockType( name );
name = config.fallbackBlockName;
blockType = getBlockType( name, config );
}

// Include in set only if type were determined.
// TODO do we ever expect there to not be an unknown type handler?
if ( blockType && ( rawContent || name !== fallbackBlock ) ) {
if ( blockType && ( rawContent || name !== config.fallbackBlockName ) ) {
// TODO allow blocks to opt-in to receiving a tree instead of a string.
// Gradually convert all blocks to this new format, then remove the
// string serialization.
const block = createBlock(
name,
blockType,
getBlockAttributes( blockType, rawContent, attributes )
);

Expand All @@ -201,17 +201,18 @@ export function createBlockWithFallback( name, rawContent, attributes ) {
* Parses the post content with a PegJS grammar and returns a list of blocks.
*
* @param {String} content The post content
* @param {Object} config Block Types config
* @return {Array} Block list
*/
export function parseWithGrammar( content ) {
export function parse( content, config ) {
return grammarParse( content ).reduce( ( memo, blockNode ) => {
const { blockName, rawContent, attrs } = blockNode;
const block = createBlockWithFallback( blockName, rawContent.trim(), attrs );
const block = createBlockWithFallback( blockName, rawContent.trim(), attrs, config );
if ( block ) {
memo.push( block );
}
return memo;
}, [] );
}

export default parseWithGrammar;
export default parse;
11 changes: 6 additions & 5 deletions blocks/api/paste.js → block-api/paste.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { nodetypes } from '@wordpress/utils';
* Internal dependencies
*/
import { createBlock } from './factory';
import { getBlockTypes, getUnknownTypeHandlerName } from './registration';
import { getBlockAttributes } from './parser';
import { getBlockType } from './config';
import stripAttributes from './paste/strip-attributes';
import removeSpans from './paste/remove-spans';

Expand Down Expand Up @@ -78,11 +78,11 @@ export function normaliseToBlockLevelNodes( nodes ) {
return Array.from( accu.childNodes );
}

export default function( nodes ) {
export default function( nodes, config ) {
const prepare = compose( [ normaliseToBlockLevelNodes, removeSpans, stripAttributes ] );

return prepare( nodes ).map( ( node ) => {
const block = getBlockTypes().reduce( ( acc, blockType ) => {
const block = config.blockTypes.reduce( ( acc, blockType ) => {
if ( acc ) {
return acc;
}
Expand All @@ -95,7 +95,7 @@ export default function( nodes ) {
}

return createBlock(
blockType.name,
blockType,
getBlockAttributes(
blockType,
node.outerHTML
Expand All @@ -107,7 +107,8 @@ export default function( nodes ) {
return block;
}

return createBlock( getUnknownTypeHandlerName(), {
const fallbackBlockType = getBlockType( config.fallbackBlockName, config );
return createBlock( fallbackBlockType, {
content: node.outerHTML,
} );
} );
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
12 changes: 7 additions & 5 deletions blocks/api/serializer.js → block-api/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Component, createElement, renderToString, cloneElement, Children } from
/**
* Internal dependencies
*/
import { getBlockType } from './registration';
import { getBlockType } from './config';

/**
* Returns the block's default classname from its name
Expand Down Expand Up @@ -131,9 +131,8 @@ export function getBeautifulContent( content ) {
} );
}

export function serializeBlock( block ) {
export function serializeBlock( block, blockType ) {
const blockName = block.name;
const blockType = getBlockType( blockName );

let saveContent;
if ( block.isValid ) {
Expand Down Expand Up @@ -169,8 +168,11 @@ export function serializeBlock( block ) {
* Takes a block or set of blocks and returns the serialized post content.
*
* @param {Array} blocks Block(s) to serialize
* @param {Object} config Block Types config
* @return {String} The post content
*/
export default function serialize( blocks ) {
return castArray( blocks ).map( serializeBlock ).join( '\n\n' );
export default function serialize( blocks, config ) {
return castArray( blocks )
.map( ( block ) => serializeBlock( block, getBlockType( block.name, config ) ) )
.join( '\n\n' );
}
File renamed without changes.
25 changes: 25 additions & 0 deletions block-api/test/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Internal dependencies
*/
import { getBlockType } from '../config';

describe( 'block types config', () => {
describe( 'getBlockType', () => {
it( 'should return undefined if the block type was not found', () => {
const blockTypes = [
{ name: 'core/text' },
];
const blockType = getBlockType( 'core/image', { blockTypes } );

expect( blockType ).toBeUndefined();
} );

it( 'should return the correponding blockType', () => {
const textBlockType = { name: 'core/text' };
const blockTypes = [ textBlockType ];
const blockType = getBlockType( 'core/text', { blockTypes } );

expect( blockType ).toBe( textBlockType );
} );
} );
} );
Loading

0 comments on commit 1f23e41

Please sign in to comment.