From e44fbd36755816b74705ad98b7917124806e7c11 Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski Date: Thu, 15 Mar 2018 10:55:13 +0100 Subject: [PATCH 1/4] Blocks: Move shared configuration for dynamic blocks to JSON files --- bin/get-server-blocks.php | 39 -------------------- blocks/library/block/index.js | 16 ++------- blocks/library/block/index.php | 10 ------ blocks/library/block/settings.json | 15 ++++++++ blocks/library/categories/index.js | 26 +------------- blocks/library/categories/index.php | 11 ------ blocks/library/categories/settings.json | 25 +++++++++++++ blocks/library/latest-posts/index.js | 8 +---- blocks/library/latest-posts/index.php | 44 ----------------------- blocks/library/latest-posts/settings.json | 41 +++++++++++++++++++++ blocks/test/full-content.js | 2 -- blocks/test/server-registered.json | 1 - gutenberg.php | 4 +++ lib/blocks.php | 24 +++++++++++++ lib/class-wp-block-type.php | 24 +++++++++++++ lib/client-assets.php | 2 +- package.json | 4 +-- test/unit/setup-blocks.js | 16 ++++++++- 18 files changed, 155 insertions(+), 157 deletions(-) delete mode 100755 bin/get-server-blocks.php create mode 100644 blocks/library/block/settings.json create mode 100644 blocks/library/categories/settings.json create mode 100644 blocks/library/latest-posts/settings.json delete mode 100644 blocks/test/server-registered.json diff --git a/bin/get-server-blocks.php b/bin/get-server-blocks.php deleted file mode 100755 index 0d3f068aa2d157..00000000000000 --- a/bin/get-server-blocks.php +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env php - null, }; diff --git a/blocks/library/block/index.php b/blocks/library/block/index.php index 98807d0bd04284..e3b30e28747be2 100644 --- a/blocks/library/block/index.php +++ b/blocks/library/block/index.php @@ -31,13 +31,3 @@ function gutenberg_render_block_core_reusable_block( $attributes ) { return gutenberg_render_block( $block ); } - -register_block_type( 'core/block', array( - 'attributes' => array( - 'ref' => array( - 'type' => 'number', - ), - ), - - 'render_callback' => 'gutenberg_render_block_core_reusable_block', -) ); diff --git a/blocks/library/block/settings.json b/blocks/library/block/settings.json new file mode 100644 index 00000000000000..b12818317aa1c3 --- /dev/null +++ b/blocks/library/block/settings.json @@ -0,0 +1,15 @@ +{ + "name": "core/block", + "category": "shared", + "isPrivate": true, + "supports": { + "customClassName": false, + "html": false + }, + "attributes": { + "ref": { + "type": "number" + } + }, + "render_callback": "gutenberg_render_block_core_reusable_block" +} diff --git a/blocks/library/categories/index.js b/blocks/library/categories/index.js index d180b7df245df7..1b3f2b01a56879 100644 --- a/blocks/library/categories/index.js +++ b/blocks/library/categories/index.js @@ -10,7 +10,7 @@ import './editor.scss'; import './style.scss'; import CategoriesBlock from './block'; -export const name = 'core/categories'; +export { name } from './settings.json'; export const settings = { title: __( 'Categories' ), @@ -19,30 +19,6 @@ export const settings = { icon: 'list-view', - category: 'widgets', - - attributes: { - showPostCounts: { - type: 'boolean', - default: false, - }, - displayAsDropdown: { - type: 'boolean', - default: false, - }, - showHierarchy: { - type: 'boolean', - default: false, - }, - align: { - type: 'string', - }, - }, - - supports: { - html: false, - }, - getEditWrapperProps( attributes ) { const { align } = attributes; if ( 'left' === align || 'right' === align || 'full' === align ) { diff --git a/blocks/library/categories/index.php b/blocks/library/categories/index.php index 61dc720fbe4590..43c54fe21152b4 100644 --- a/blocks/library/categories/index.php +++ b/blocks/library/categories/index.php @@ -83,14 +83,3 @@ function onCatChange() { 'render_block_core_categories', - ) ); -} - -add_action( 'init', 'register_block_core_categories' ); diff --git a/blocks/library/categories/settings.json b/blocks/library/categories/settings.json new file mode 100644 index 00000000000000..4f0b9fc60b6a2a --- /dev/null +++ b/blocks/library/categories/settings.json @@ -0,0 +1,25 @@ +{ + "name": "core/categories", + "category": "widgets", + "supports": { + "html": false + }, + "attributes": { + "showPostCounts": { + "type": "boolean", + "default": false + }, + "displayAsDropdown": { + "type": "boolean", + "default": false + }, + "showHierarchy": { + "type": "boolean", + "default": false + }, + "align": { + "type": "string" + } + }, + "render_callback": "render_block_core_categories" +} diff --git a/blocks/library/latest-posts/index.js b/blocks/library/latest-posts/index.js index 3762b61e2a68b9..30353328f67a32 100644 --- a/blocks/library/latest-posts/index.js +++ b/blocks/library/latest-posts/index.js @@ -10,7 +10,7 @@ import './editor.scss'; import './style.scss'; import LatestPostsBlock from './block'; -export const name = 'core/latest-posts'; +export { name } from './settings.json'; export const settings = { title: __( 'Latest Posts' ), @@ -19,14 +19,8 @@ export const settings = { icon: 'list-view', - category: 'widgets', - keywords: [ __( 'recent posts' ) ], - supports: { - html: false, - }, - getEditWrapperProps( attributes ) { const { align } = attributes; if ( 'left' === align || 'right' === align || 'wide' === align || 'full' === align ) { diff --git a/blocks/library/latest-posts/index.php b/blocks/library/latest-posts/index.php index bb06d4b4fe5a5e..c18afb26e38ba7 100644 --- a/blocks/library/latest-posts/index.php +++ b/blocks/library/latest-posts/index.php @@ -64,47 +64,3 @@ function render_block_core_latest_posts( $attributes ) { return $block_content; } - -/** - * Registers the `core/latest-posts` block on server. - */ -function register_block_core_latest_posts() { - register_block_type( 'core/latest-posts', array( - 'attributes' => array( - 'categories' => array( - 'type' => 'string', - ), - 'postsToShow' => array( - 'type' => 'number', - 'default' => 5, - ), - 'displayPostDate' => array( - 'type' => 'boolean', - 'default' => false, - ), - 'postLayout' => array( - 'type' => 'string', - 'default' => 'list', - ), - 'columns' => array( - 'type' => 'number', - 'default' => 3, - ), - 'align' => array( - 'type' => 'string', - 'default' => 'center', - ), - 'order' => array( - 'type' => 'string', - 'default' => 'desc', - ), - 'orderBy' => array( - 'type' => 'string', - 'default' => 'date', - ), - ), - 'render_callback' => 'render_block_core_latest_posts', - ) ); -} - -add_action( 'init', 'register_block_core_latest_posts' ); diff --git a/blocks/library/latest-posts/settings.json b/blocks/library/latest-posts/settings.json new file mode 100644 index 00000000000000..8d01ee581a31eb --- /dev/null +++ b/blocks/library/latest-posts/settings.json @@ -0,0 +1,41 @@ +{ + "name": "core/latest-posts", + "category": "widgets", + "supports": { + "html": false + }, + "attributes": { + "categories": { + "type": "string" + }, + "postsToShow": { + "type": "number", + "default": 5 + }, + "displayPostDate": { + "type": "boolean", + "default": false + }, + "postLayout": { + "type": "string", + "default": "list" + }, + "columns": { + "type": "number", + "default": 3 + }, + "align": { + "type": "string", + "default": "center" + }, + "order": { + "type": "string", + "default": "desc" + }, + "orderBy": { + "type": "string", + "default": "date" + } + }, + "render_callback": "render_block_core_latest_posts" +} diff --git a/blocks/test/full-content.js b/blocks/test/full-content.js index 24341839da5c36..af0c081a8c2d0a 100644 --- a/blocks/test/full-content.js +++ b/blocks/test/full-content.js @@ -96,8 +96,6 @@ function normalizeParsedBlocks( blocks ) { describe( 'full post content fixture', () => { beforeAll( () => { - window._wpBlocks = require( './server-registered.json' ); - // Load all hooks that modify blocks require( 'blocks/hooks' ); registerCoreBlocks(); diff --git a/blocks/test/server-registered.json b/blocks/test/server-registered.json deleted file mode 100644 index 8f668f320d4ff6..00000000000000 --- a/blocks/test/server-registered.json +++ /dev/null @@ -1 +0,0 @@ -{"core\/block":{"attributes":{"ref":{"type":"number"}}},"core\/latest-posts":{"attributes":{"categories":{"type":"string"},"postsToShow":{"type":"number","default":5},"displayPostDate":{"type":"boolean","default":false},"postLayout":{"type":"string","default":"list"},"columns":{"type":"number","default":3},"align":{"type":"string","default":"center"},"order":{"type":"string","default":"desc"},"orderBy":{"type":"string","default":"date"}}}} \ No newline at end of file diff --git a/gutenberg.php b/gutenberg.php index a6b3eefa4467b2..e64ea38b7f6c42 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -158,6 +158,10 @@ function gutenberg_init( $return, $post ) { return false; } + foreach ( glob( dirname( __FILE__ ) . '/blocks/library/*/settings.json' ) as $block_settings_file ) { + register_block_type_from_settings( $block_settings_file ); + } + add_action( 'admin_enqueue_scripts', 'gutenberg_editor_scripts_and_styles' ); add_filter( 'screen_options_show_screen', '__return_false' ); add_filter( 'admin_body_class', 'gutenberg_add_admin_body_class' ); diff --git a/lib/blocks.php b/lib/blocks.php index e28971bad032c2..97f63545c929c6 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -30,6 +30,30 @@ function register_block_type( $name, $args = array() ) { return WP_Block_Type_Registry::get_instance()->register( $name, $args ); } +/** + * Registers a block type from the JSON settings file. + * + * @since 2.5.0 + * + * @param string $file_name The file name with block settings. + * @return WP_Block_Type|false The registered block type on success, or false on failure. + */ +function register_block_type_from_settings( $file_name ) { + if ( ! file_exists( $file_name ) ) { + return false; + } + + $file_content = file_get_contents( $file_name ); + $settings = json_decode( $file_content, true ); + // TODO: validate JSON file schema instead + if ( empty( $settings['name'] ) ) { + return false; + } + $name = $settings['name']; + unset( $settings['name'] ); + + return register_block_type( $name, $settings ); +} /** * Unregisters a block type. * diff --git a/lib/class-wp-block-type.php b/lib/class-wp-block-type.php index 39f08e54fddc28..b2148be728df0b 100644 --- a/lib/class-wp-block-type.php +++ b/lib/class-wp-block-type.php @@ -22,6 +22,22 @@ class WP_Block_Type { */ public $name; + /** + * Block type category name. + * + * @since 2.5.0 + * @var string + */ + public $category; + + /** + * Block type icon name. + * + * @since 2.5.0 + * @var string + */ + public $icon; + /** * Block type render callback. * @@ -30,6 +46,14 @@ class WP_Block_Type { */ public $render_callback; + /** + * Block type supports property schemas. + * + * @since 2.5.0 + * @var array + */ + public $supports; + /** * Block type attributes property schemas. * diff --git a/lib/client-assets.php b/lib/client-assets.php index a24279f6b7b59a..4c42288a3fe5dd 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -656,7 +656,7 @@ function gutenberg_get_post_to_edit( $post_id ) { function gutenberg_prepare_blocks_for_js() { $block_registry = WP_Block_Type_Registry::get_instance(); $blocks = array(); - $keys_to_pick = array( 'title', 'icon', 'category', 'keywords', 'supports', 'attributes' ); + $keys_to_pick = array( 'title', 'icon', 'category', 'keywords', 'isPrivate', 'supports', 'attributes' ); foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) { foreach ( $keys_to_pick as $key ) { diff --git a/package.json b/package.json index f841d7ae6c3c8c..4067437380c471 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "expose-loader": "0.7.3", "extract-text-webpack-plugin": "3.0.0", "gettext-parser": "1.3.0", + "glob": "7.1.2", "node-sass": "4.7.2", "pegjs": "0.10.0", "pegjs-loader": "0.5.4", @@ -139,8 +140,7 @@ "test-php": "npm run lint-php && npm run test-unit-php", "ci": "concurrently \"npm run lint && npm run build\" \"npm run test-unit:coverage-ci\"", "fixtures:clean": "rimraf \"blocks/test/fixtures/*.+(json|serialized.html)\"", - "fixtures:server-registered": "docker-compose run -w /var/www/html/wp-content/plugins/gutenberg --rm wordpress ./bin/get-server-blocks.php > blocks/test/server-registered.json", - "fixtures:generate": "npm run fixtures:server-registered && cross-env GENERATE_MISSING_FIXTURES=y npm run test-unit", + "fixtures:generate": "cross-env GENERATE_MISSING_FIXTURES=y npm run test-unit", "fixtures:regenerate": "npm run fixtures:clean && npm run fixtures:generate", "package-plugin": "./bin/build-plugin-zip.sh", "pot-to-php": "./bin/pot-to-php.js", diff --git a/test/unit/setup-blocks.js b/test/unit/setup-blocks.js index 5bab169511a653..aeaeea96f7baae 100644 --- a/test/unit/setup-blocks.js +++ b/test/unit/setup-blocks.js @@ -1,2 +1,16 @@ +/** + * External dependencies + */ +const glob = require( 'glob' ); + // Bootstrap server-registered blocks -global.window._wpBlocks = require( 'blocks/test/server-registered.json' ); +global.window._wpBlocks = glob. + sync( 'blocks/library/*/settings.json' ). + reduce( ( blocks, fileName ) => { + const { name, ...settings } = require( fileName ); + + return { + ...blocks, + [ name ]: settings, + }; + }, {} ); From faf603f94be6eb00a04a02768f1629591dc0c7b5 Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski Date: Thu, 15 Mar 2018 12:54:24 +0100 Subject: [PATCH 2/4] Blocks: Filter all setting but those which were registered server-side --- blocks/api/registration.js | 21 +++++++++++++-------- blocks/api/test/registration.js | 16 +++------------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/blocks/api/registration.js b/blocks/api/registration.js index 3bc071809f9c90..f94d0f55c25bd9 100644 --- a/blocks/api/registration.js +++ b/blocks/api/registration.js @@ -88,12 +88,6 @@ const POST_FORMAT_BLOCK_MAP = { * otherwise `undefined`. */ export function registerBlockType( name, settings ) { - settings = { - name, - ...get( window._wpBlocks, name ), - ...settings, - }; - if ( typeof name !== 'string' ) { console.error( 'Block names must be strings.' @@ -112,10 +106,21 @@ export function registerBlockType( name, settings ) { ); return; } + if ( typeof settings !== 'object' ) { + console.error( + 'Block settings must be objects.' + ); + return; + } - settings = applyFilters( 'blocks.registerBlockType', settings, name ); + settings = { + name, + ...applyFilters( 'blocks.registerBlockType', settings, name ), + // the values set server-side can't be updated on the client + ...get( window._wpBlocks, name, {} ), + }; - if ( ! settings || ! isFunction( settings.save ) ) { + if ( ! isFunction( settings.save ) ) { console.error( 'The "save" property must be specified and must be a valid function.' ); diff --git a/blocks/api/test/registration.js b/blocks/api/test/registration.js index af9378ca61aa94..08e58aab3d25b8 100644 --- a/blocks/api/test/registration.js +++ b/blocks/api/test/registration.js @@ -108,7 +108,7 @@ describe( 'blocks', () => { it( 'should reject blocks without a save function', () => { const block = registerBlockType( 'my-plugin/fancy-block-5' ); - expect( console ).toHaveErroredWith( 'The "save" property must be specified and must be a valid function.' ); + expect( console ).toHaveErroredWith( 'Block settings must be objects.' ); expect( block ).toBeUndefined(); } ); @@ -161,7 +161,7 @@ describe( 'blocks', () => { expect( block ).toBeUndefined(); } ); - it( 'should default to browser-initialized global attributes', () => { + it( 'should not update server-initialized attributes', () => { const attributes = { ok: { type: 'boolean' } }; window._wpBlocks = { 'core/test-block-with-attributes': { attributes }, @@ -176,17 +176,7 @@ describe( 'blocks', () => { category: 'common', title: 'block title', icon: 'block-default', - attributes: { - ok: { - type: 'boolean', - }, - className: { - type: 'string', - }, - layout: { - type: 'string', - }, - }, + attributes, } ); } ); From 1f31b6be8034533a5be58a000e7ab027484226f7 Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski Date: Thu, 15 Mar 2018 12:56:06 +0100 Subject: [PATCH 3/4] Blocks: Move registration server side for the paragraph block --- blocks/library/paragraph/index.js | 70 ++++++++++---------------- blocks/library/paragraph/settings.json | 37 ++++++++++++++ 2 files changed, 63 insertions(+), 44 deletions(-) create mode 100644 blocks/library/paragraph/settings.json diff --git a/blocks/library/paragraph/index.js b/blocks/library/paragraph/index.js index 93ab1c019c68bd..7e4880ca2e1029 100644 --- a/blocks/library/paragraph/index.js +++ b/blocks/library/paragraph/index.js @@ -209,42 +209,7 @@ class ParagraphBlock extends Component { } } -const supports = { - className: false, -}; - -const schema = { - content: { - type: 'array', - source: 'children', - selector: 'p', - default: [], - }, - align: { - type: 'string', - }, - dropCap: { - type: 'boolean', - default: false, - }, - placeholder: { - type: 'string', - }, - width: { - type: 'string', - }, - textColor: { - type: 'string', - }, - backgroundColor: { - type: 'string', - }, - fontSize: { - type: 'number', - }, -}; - -export const name = 'core/paragraph'; +export { name } from './settings.json'; export const settings = { title: __( 'Paragraph' ), @@ -253,14 +218,8 @@ export const settings = { icon: 'editor-paragraph', - category: 'common', - keywords: [ __( 'text' ) ], - supports, - - attributes: schema, - transforms: { from: [ { @@ -276,13 +235,36 @@ export const settings = { deprecated: [ { - supports, + supports: { + className: false, + }, attributes: { - ...schema, content: { type: 'string', source: 'html', }, + align: { + type: 'string', + }, + dropCap: { + type: 'boolean', + default: false, + }, + placeholder: { + type: 'string', + }, + width: { + type: 'string', + }, + textColor: { + type: 'string', + }, + backgroundColor: { + type: 'string', + }, + fontSize: { + type: 'number', + }, }, save( { attributes } ) { return attributes.content; diff --git a/blocks/library/paragraph/settings.json b/blocks/library/paragraph/settings.json new file mode 100644 index 00000000000000..cca7f92d939a2e --- /dev/null +++ b/blocks/library/paragraph/settings.json @@ -0,0 +1,37 @@ +{ + "name": "core/paragraph", + "category": "common", + "supports": { + "className": false + }, + "attributes": { + "content": { + "type": "array", + "source": "children", + "selector": "p", + "default": [] + }, + "align": { + "type": "string" + }, + "dropCap": { + "type": "boolean", + "default": false + }, + "placeholder": { + "type": "string" + }, + "width": { + "type": "string" + }, + "textColor": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "fontSize": { + "type": "number" + } + } +} From 3d229425f7bcb77315b9174689c39549de59e568 Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski Date: Thu, 15 Mar 2018 12:56:49 +0100 Subject: [PATCH 4/4] Blocks: Move registration server side for all embed blocks --- blocks/library/embed/index.js | 27 +------------- blocks/library/embed/settings.json | 59 ++++++++++++++++++++++++++++++ lib/blocks.php | 17 ++++++++- test/unit/setup-blocks.js | 14 +++++-- 4 files changed, 87 insertions(+), 30 deletions(-) create mode 100644 blocks/library/embed/settings.json diff --git a/blocks/library/embed/index.js b/blocks/library/embed/index.js index 5b0c80628e6f27..2865ba9bd5cbc5 100644 --- a/blocks/library/embed/index.js +++ b/blocks/library/embed/index.js @@ -26,7 +26,7 @@ import BlockAlignmentToolbar from '../../block-alignment-toolbar'; // These embeds do not work in sandboxes const HOSTS_NO_PREVIEWS = [ 'facebook.com' ]; -function getEmbedBlockSettings( { title, icon, category = 'embed', transforms, keywords = [] } ) { +function getEmbedBlockSettings( { title, icon, transforms, keywords = [] } ) { return { title, @@ -34,31 +34,8 @@ function getEmbedBlockSettings( { title, icon, category = 'embed', transforms, k icon, - category, - keywords, - attributes: { - url: { - type: 'string', - }, - caption: { - type: 'array', - source: 'children', - selector: 'figcaption', - default: [], - }, - align: { - type: 'string', - }, - type: { - type: 'string', - }, - providerNameSlug: { - type: 'string', - }, - }, - transforms, getEditWrapperProps( attributes ) { @@ -255,7 +232,7 @@ function getEmbedBlockSettings( { title, icon, category = 'embed', transforms, k }; } -export const name = 'core/embed'; +export { name } from './settings.json'; export const settings = getEmbedBlockSettings( { title: __( 'Embed' ), diff --git a/blocks/library/embed/settings.json b/blocks/library/embed/settings.json new file mode 100644 index 00000000000000..c1456e26339dd9 --- /dev/null +++ b/blocks/library/embed/settings.json @@ -0,0 +1,59 @@ +{ + "name": "core/embed", + "category": "embed", + "attributes": { + "url": { + "type": "string" + }, + "caption": { + "type": "array", + "source": "children", + "selector": "figcaption", + "default": [] + }, + "align": { + "type": "string" + }, + "type": { + "type": "string" + }, + "providerNameSlug": { + "type": "string" + } + }, + "variations": [ + "core-embed/twitter", + "core-embed/youtube", + "core-embed/facebook", + "core-embed/instagram", + "core-embed/wordpress", + "core-embed/soundcloud", + "core-embed/spotify", + "core-embed/flickr", + "core-embed/vimeo", + "core-embed/animoto", + "core-embed/cloudup", + "core-embed/collegehumor", + "core-embed/dailymotion", + "core-embed/funnyordie", + "core-embed/hulu", + "core-embed/imgur", + "core-embed/issuu", + "core-embed/kickstarter", + "core-embed/meetup-com", + "core-embed/mixcloud", + "core-embed/photobucket", + "core-embed/polldaddy", + "core-embed/reddit", + "core-embed/reverbnation", + "core-embed/screencast", + "core-embed/scribd", + "core-embed/slideshare", + "core-embed/smugmug", + "core-embed/speaker", + "core-embed/ted", + "core-embed/tumblr", + "core-embed/videopress", + "core-embed/wordpress-tv" + ] +} diff --git a/lib/blocks.php b/lib/blocks.php index 97f63545c929c6..0fd52e51f81be9 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -31,7 +31,7 @@ function register_block_type( $name, $args = array() ) { } /** - * Registers a block type from the JSON settings file. + * Registers a block type(s) from the JSON settings file. * * @since 2.5.0 * @@ -52,8 +52,21 @@ function register_block_type_from_settings( $file_name ) { $name = $settings['name']; unset( $settings['name'] ); - return register_block_type( $name, $settings ); + // Find if there are different variations of the given block type, e.g. embed + $variations = array(); + if ( ! empty( $settings['variations'] ) ) { + $variations = $settings['variations']; + unset( $settings['variations'] ); + } + + $result = register_block_type( $name, $settings ); + foreach ( $variations as $variation_name ) { + register_block_type( $variation_name, $settings ); + } + + return $result; } + /** * Unregisters a block type. * diff --git a/test/unit/setup-blocks.js b/test/unit/setup-blocks.js index aeaeea96f7baae..d07c4b8c994241 100644 --- a/test/unit/setup-blocks.js +++ b/test/unit/setup-blocks.js @@ -6,11 +6,19 @@ const glob = require( 'glob' ); // Bootstrap server-registered blocks global.window._wpBlocks = glob. sync( 'blocks/library/*/settings.json' ). - reduce( ( blocks, fileName ) => { - const { name, ...settings } = require( fileName ); + reduce( ( blockTypes, fileName ) => { + const { name, variations = [], ...settings } = require( fileName ); + + const blockTypeVariations = variations.reduce( ( memo, variationName ) => { + return { + ...memo, + [ variationName ]: settings, + }; + }, {} ); return { - ...blocks, + ...blockTypes, [ name ]: settings, + ...blockTypeVariations, }; }, {} );