From 1550d1c96bbd364317bc1aa285dc64c62055ec73 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Mon, 12 Jun 2017 22:50:16 +0200 Subject: [PATCH 01/12] Refactor serializer: rearrange code for clarity and introspection These changes are intended to make the flow of transformation from in-memory block data to serialized `post_content` clearer. Additionally they are intended to ease testing and create points for easy trapping and transformation of the data through the pipeline. --- blocks/api/serializer.js | 90 +++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js index 5ccd305befc45..56b3c4d0bd3d6 100644 --- a/blocks/api/serializer.js +++ b/blocks/api/serializer.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { difference } from 'lodash'; +import { isEmpty, map, reduce, some } from 'lodash'; import { html as beautifyHtml } from 'js-beautify'; /** @@ -37,34 +37,45 @@ 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 attributes which ought to be saved and serialized * - * @param {Object} realAttributes Actual block attributes - * @param {Object} expectedAttributes Expected block attributes - * @return {string} Comment attributes + * When a block exists in memory it contains as its attributes + * both those which come from the block comment opening _and_ + * those which come from parsing the contents of the block. + * Additionally they may live in a form fine for memory but + * which isn't valid for serialization. + * + * This function returns a filtered set of attributes which are + * the ones which need to be saved in order to ensure a full + * serialization and they are in a form which is safe to store. + * + * @param {Object} allAttributes Attributes from in-memory block data + * @param {Object} fromContent Attributes which are inferred from block content + * @returns {Object} filtered set of attributes for minimum safe save/serialization */ -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 attributesToSave( allAttributes, fromContent ) { + // Reasons an attribute need not be saved + const canBeInferred = key => fromContent.hasOwnProperty( key ); + const isUndefined = key => undefined === allAttributes[ key ]; - // Serialize the comment attributes as `key="value"`. - return keys.reduce( ( memo, key ) => { - const value = realAttributes[ key ]; - if ( undefined === value ) { - return memo; - } + const isValid = key => ! some( [ + isUndefined, + canBeInferred, + ], f => f( key ) ); - if ( 'string' === typeof value ) { - return memo + `${ key }="${ value.replace( '"', '\"' ) }" `; - } + // Specific ways we need to transform the values of saved attributes + const escapeDoubleQuotes = value => 'string' === typeof value + ? value.replace( '"', '\"' ) + : value; - return memo + `${ key }="${ value }" `; - }, '' ); + const transform = key => escapeDoubleQuotes( allAttributes[ key ] ); + + // Iterate over attributes and produce the set to save + return reduce( + Object.keys( allAttributes ), + ( toSave, key ) => Object.assign( toSave, isValid( key ) && { [ key ]: transform( key ) } ), + {}, + ); } /** @@ -74,30 +85,31 @@ export function getCommentAttributes( realAttributes, expectedAttributes ) { * @return {String} The post content */ export default function serialize( blocks ) { - return blocks.reduce( ( memo, block ) => { + return blocks + .map( block => { const blockName = block.name; const blockType = getBlockType( blockName ); const saveContent = getSaveContent( blockType.save, block.attributes ); + const saveAttributes = attributesToSave( block.attributes, parseBlockAttributes( saveContent, blockType ) ); + const beautifyOptions = { indent_inner_html: true, wrap_line_length: 0, }; - const blockAttributes = getCommentAttributes( block.attributes, parseBlockAttributes( saveContent, blockType ) ); + + const serializedAttributes = ! isEmpty( saveAttributes ) + ? map( saveAttributes, ( value, key ) => `${ key }="${ value }"` ).join( ' ' ) + ' ' + : ''; if ( ! saveContent ) { - return memo + '\n\n'; + return ``; } - return memo + ( - '' + - '\n' + beautifyHtml( saveContent, beautifyOptions ) + '\n' + - '' - ) + '\n\n'; - }, '' ); + return [ + ``, + beautifyHtml( saveContent, beautifyOptions ), + ``, + ].join( '\n' ); + } ) + .join( '\n\n' ); } From 1166e686da9d358c2b2a54114c304a9870f96bdb Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Tue, 13 Jun 2017 21:49:54 +0200 Subject: [PATCH 02/12] Updates from feedback --- blocks/api/post.pegjs | 4 +-- blocks/api/serializer.js | 53 ++++++++++++++++++++++------------------ 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/blocks/api/post.pegjs b/blocks/api/post.pegjs index 3f3c9e9330710..79bf7d13dbd2f 100644 --- a/blocks/api/post.pegjs +++ b/blocks/api/post.pegjs @@ -78,9 +78,9 @@ HTML_Attribute_Unquoted { return keyValue( name, value ) } HTML_Attribute_Quoted - = name:HTML_Attribute_Name _* "=" _* '"' value:$(('\\"' . / !'"' .)*) '"' + = name:HTML_Attribute_Name _* "=" _* '"' value:$(("\\" '"' . / !'"' .)*) '"' { return keyValue( name, value ) } - / name:HTML_Attribute_Name _* "=" _* "'" value:$(("\\'" . / !"'" .)*) "'" + / name:HTML_Attribute_Name _* "=" _* "'" value:$(("\\" "'" . / !"'" .)*) "'" { return keyValue( name, value ) } HTML_Attribute_Name diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js index 56b3c4d0bd3d6..bc511afb1b8a0 100644 --- a/blocks/api/serializer.js +++ b/blocks/api/serializer.js @@ -36,11 +36,28 @@ export function getSaveContent( save, attributes ) { return wp.element.renderToString( rawContent ); } +// Reasons an attribute shouldn't be stored in the block header +const canBeInferred = ( allValue, contentValue ) => undefined !== contentValue; +const isUndefined = allValue => undefined === allValue; + +const isValidForHeader = ( allValue, contentValue ) => ! some( [ + isUndefined, + canBeInferred, +], f => f( allValue, contentValue ) ); + +// Specific ways we need to transform attributes for storage in the block header +const escapeDoubleQuotes = value => 'string' === typeof value + ? value.replace( /"/g, '\"' ) + : value; + +const transformAttributeValue = escapeDoubleQuotes; + /** - * Returns attributes which ought to be saved and serialized + * Returns attributes which ought to be saved + * and serialized into the block comment header * * When a block exists in memory it contains as its attributes - * both those which come from the block comment opening _and_ + * both those which come from the block comment header _and_ * those which come from parsing the contents of the block. * Additionally they may live in a form fine for memory but * which isn't valid for serialization. @@ -49,31 +66,19 @@ export function getSaveContent( save, attributes ) { * the ones which need to be saved in order to ensure a full * serialization and they are in a form which is safe to store. * - * @param {Object} allAttributes Attributes from in-memory block data - * @param {Object} fromContent Attributes which are inferred from block content + * @param {Object} allAttributes Attributes from in-memory block data + * @param {Object} attributesFromContent Attributes which are inferred from block content * @returns {Object} filtered set of attributes for minimum safe save/serialization */ -export function attributesToSave( allAttributes, fromContent ) { - // Reasons an attribute need not be saved - const canBeInferred = key => fromContent.hasOwnProperty( key ); - const isUndefined = key => undefined === allAttributes[ key ]; - - const isValid = key => ! some( [ - isUndefined, - canBeInferred, - ], f => f( key ) ); - - // Specific ways we need to transform the values of saved attributes - const escapeDoubleQuotes = value => 'string' === typeof value - ? value.replace( '"', '\"' ) - : value; - - const transform = key => escapeDoubleQuotes( allAttributes[ key ] ); - +export function getCommentAttributes( allAttributes, attributesFromContent ) { // Iterate over attributes and produce the set to save return reduce( Object.keys( allAttributes ), - ( toSave, key ) => Object.assign( toSave, isValid( key ) && { [ key ]: transform( key ) } ), + ( toSave, key ) => { + return isValidForHeader( allAttributes[ key ], attributesFromContent[ key ] ) + ? Object.assign( toSave, { [ key ]: transformAttributeValue( allAttributes[ key ] ) } ) + : toSave; + }, {}, ); } @@ -90,7 +95,7 @@ export default function serialize( blocks ) { const blockName = block.name; const blockType = getBlockType( blockName ); const saveContent = getSaveContent( blockType.save, block.attributes ); - const saveAttributes = attributesToSave( block.attributes, parseBlockAttributes( saveContent, blockType ) ); + const saveAttributes = getCommentAttributes( block.attributes, parseBlockAttributes( saveContent, blockType ) ); const beautifyOptions = { indent_inner_html: true, @@ -106,7 +111,7 @@ export default function serialize( blocks ) { } return [ - ``, + ``, beautifyHtml( saveContent, beautifyOptions ), ``, ].join( '\n' ); From d495cf7f8395995ec97c7f38e9505eaff02c203d Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Tue, 13 Jun 2017 22:02:03 +0200 Subject: [PATCH 03/12] inline functions --- blocks/api/serializer.js | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js index bc511afb1b8a0..0c755ce72d167 100644 --- a/blocks/api/serializer.js +++ b/blocks/api/serializer.js @@ -36,22 +36,10 @@ export function getSaveContent( save, attributes ) { return wp.element.renderToString( rawContent ); } -// Reasons an attribute shouldn't be stored in the block header -const canBeInferred = ( allValue, contentValue ) => undefined !== contentValue; -const isUndefined = allValue => undefined === allValue; - -const isValidForHeader = ( allValue, contentValue ) => ! some( [ - isUndefined, - canBeInferred, -], f => f( allValue, contentValue ) ); - -// Specific ways we need to transform attributes for storage in the block header const escapeDoubleQuotes = value => 'string' === typeof value ? value.replace( /"/g, '\"' ) : value; -const transformAttributeValue = escapeDoubleQuotes; - /** * Returns attributes which ought to be saved * and serialized into the block comment header @@ -75,8 +63,11 @@ export function getCommentAttributes( allAttributes, attributesFromContent ) { return reduce( Object.keys( allAttributes ), ( toSave, key ) => { - return isValidForHeader( allAttributes[ key ], attributesFromContent[ key ] ) - ? Object.assign( toSave, { [ key ]: transformAttributeValue( allAttributes[ key ] ) } ) + const allValue = allAttributes[ key ]; + const contentValue = attributesFromContent[ key ]; + + return ! ( contentValue !== undefined || allValue === undefined ) + ? Object.assign( toSave, { [ key ]: escapeDoubleQuotes( allValue ) } ) : toSave; }, {}, From a05d749826802c2fb6f1bac43357159f5ff0ef90 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Tue, 13 Jun 2017 22:04:35 +0200 Subject: [PATCH 04/12] add explanatory comment --- blocks/api/serializer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js index 0c755ce72d167..67d6897f3598c 100644 --- a/blocks/api/serializer.js +++ b/blocks/api/serializer.js @@ -66,6 +66,7 @@ export function getCommentAttributes( allAttributes, attributesFromContent ) { const allValue = allAttributes[ key ]; const contentValue = attributesFromContent[ key ]; + // save only if attribute if not inferred from the content and if valued return ! ( contentValue !== undefined || allValue === undefined ) ? Object.assign( toSave, { [ key ]: escapeDoubleQuotes( allValue ) } ) : toSave; From 24fdbe2bb5899995bb1d606837064c950bf1ba3c Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Tue, 13 Jun 2017 22:06:59 +0200 Subject: [PATCH 05/12] styling and comment --- blocks/api/serializer.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js index 67d6897f3598c..bf05647227c7f 100644 --- a/blocks/api/serializer.js +++ b/blocks/api/serializer.js @@ -84,27 +84,28 @@ export function getCommentAttributes( allAttributes, attributesFromContent ) { export default function serialize( blocks ) { return blocks .map( block => { - const blockName = block.name; - const blockType = getBlockType( blockName ); - const saveContent = getSaveContent( blockType.save, block.attributes ); + const blockName = block.name; + const blockType = getBlockType( blockName ); + const saveContent = getSaveContent( blockType.save, block.attributes ); const saveAttributes = getCommentAttributes( block.attributes, parseBlockAttributes( saveContent, blockType ) ); - const beautifyOptions = { - indent_inner_html: true, - wrap_line_length: 0, - }; - const serializedAttributes = ! isEmpty( saveAttributes ) ? map( saveAttributes, ( value, key ) => `${ key }="${ value }"` ).join( ' ' ) + ' ' : ''; - if ( ! saveContent ) { + if ( ! saveContent ) { return ``; - } + } return [ ``, - beautifyHtml( saveContent, beautifyOptions ), + + /** make more readable - @see https://github.com/WordPress/gutenberg/pull/663 */ + beautifyHtml( saveContent, { + indent_inner_html: true, + wrap_line_length: 0, + } ), + ``, ].join( '\n' ); } ) From 7e8c2c9a32d346e3e1561d6d2f61c29a9d01ebfc Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Tue, 13 Jun 2017 22:37:19 +0200 Subject: [PATCH 06/12] moar updates --- blocks/api/post.pegjs | 6 ++-- blocks/api/serializer.js | 68 +++++++++++++++++++++++----------------- post-content.js | 2 +- 3 files changed, 44 insertions(+), 32 deletions(-) diff --git a/blocks/api/post.pegjs b/blocks/api/post.pegjs index 79bf7d13dbd2f..15bcce0713e8d 100644 --- a/blocks/api/post.pegjs +++ b/blocks/api/post.pegjs @@ -45,7 +45,7 @@ WP_Block_Html } WP_Block_Start - = "" + = "" { return { blockName: blockName, attrs: attrs @@ -78,9 +78,9 @@ HTML_Attribute_Unquoted { return keyValue( name, value ) } HTML_Attribute_Quoted - = name:HTML_Attribute_Name _* "=" _* '"' value:$(("\\" '"' . / !'"' .)*) '"' + = name:HTML_Attribute_Name _* "=" _* '"' value:$(('\\"' . / !'"' .)*) '"' { return keyValue( name, value ) } - / name:HTML_Attribute_Name _* "=" _* "'" value:$(("\\" "'" . / !"'" .)*) "'" + / name:HTML_Attribute_Name _* "=" _* "'" value:$(("\\'" . / !"'" .)*) "'" { return keyValue( name, value ) } HTML_Attribute_Name diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js index bf05647227c7f..93ee6c461c340 100644 --- a/blocks/api/serializer.js +++ b/blocks/api/serializer.js @@ -75,6 +75,45 @@ export function getCommentAttributes( allAttributes, attributesFromContent ) { ); } +/** + * Lodash iterator which transforms a key: value + * pair into a string of `key="value"` + * + * @param {*} value value to be stringified + * @param {String} key name of value + * @returns {string} stringified equality pair + */ +function asNameValuePair( value, key ) { + return `${ key }="${ value }`; +} + +export function serializeBlock( block ) { + const blockName = block.name; + const blockType = getBlockType( blockName ); + const saveContent = getSaveContent( blockType.save, block.attributes ); + const saveAttributes = getCommentAttributes( block.attributes, parseBlockAttributes( saveContent, blockType ) ); + + const serializedAttributes = ! isEmpty( saveAttributes ) + ? map( saveAttributes, asNameValuePair ).join( ' ' ) + ' ' + : ''; + + if ( ! saveContent ) { + return ``; + } + + return ( + `\n` + + + /** make more readable - @see https://github.com/WordPress/gutenberg/pull/663 */ + beautifyHtml( saveContent, { + indent_inner_html: true, + wrap_line_length: 0, + } ) + + + `\n` + ); +} + /** * Takes a block list and returns the serialized post content. * @@ -82,32 +121,5 @@ export function getCommentAttributes( allAttributes, attributesFromContent ) { * @return {String} The post content */ export default function serialize( blocks ) { - return blocks - .map( block => { - const blockName = block.name; - const blockType = getBlockType( blockName ); - const saveContent = getSaveContent( blockType.save, block.attributes ); - const saveAttributes = getCommentAttributes( block.attributes, parseBlockAttributes( saveContent, blockType ) ); - - const serializedAttributes = ! isEmpty( saveAttributes ) - ? map( saveAttributes, ( value, key ) => `${ key }="${ value }"` ).join( ' ' ) + ' ' - : ''; - - if ( ! saveContent ) { - return ``; - } - - return [ - ``, - - /** make more readable - @see https://github.com/WordPress/gutenberg/pull/663 */ - beautifyHtml( saveContent, { - indent_inner_html: true, - wrap_line_length: 0, - } ), - - ``, - ].join( '\n' ); - } ) - .join( '\n\n' ); + return blocks.map( serializeBlock ).join( '\n\n' ); } diff --git a/post-content.js b/post-content.js index 8317d2b64ae3a..3905a9a75b0cd 100644 --- a/post-content.js +++ b/post-content.js @@ -7,7 +7,7 @@ window._wpGutenbergPost = { }, content: { raw: [ - '', + '', '

The goal of this new editor is to make adding rich content to WordPress simple and enjoyable. This whole post is composed of pieces of content—somewhat similar to LEGO bricks—that you can move around and interact with. Move your cursor around and you\'ll notice the different blocks light up with outlines and arrows. Press the arrows to reposition blocks quickly, without fearing about losing things in the process of copying and pasting.

', '

What you are reading now is a text block, the most basic block of all. A text block can have multiple paragraphs, if that\'s how you prefer to write your posts. But you can also split it by hitting enter twice. Once blocks are split they get their own controls to be moved freely around the post...

', '', From 7d82605d9a2263588d5d6de7e5a8b24486a8a957 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Tue, 13 Jun 2017 22:39:42 +0200 Subject: [PATCH 07/12] feedback updates --- blocks/api/serializer.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js index 93ee6c461c340..eacf14f2d1d4e 100644 --- a/blocks/api/serializer.js +++ b/blocks/api/serializer.js @@ -47,16 +47,14 @@ const escapeDoubleQuotes = value => 'string' === typeof value * When a block exists in memory it contains as its attributes * both those which come from the block comment header _and_ * those which come from parsing the contents of the block. - * Additionally they may live in a form fine for memory but - * which isn't valid for serialization. * - * This function returns a filtered set of attributes which are - * the ones which need to be saved in order to ensure a full - * serialization and they are in a form which is safe to store. + * This function returns only those attributes which are + * needed to persist and which cannot already be inferred + * from the block content. * * @param {Object} allAttributes Attributes from in-memory block data * @param {Object} attributesFromContent Attributes which are inferred from block content - * @returns {Object} filtered set of attributes for minimum safe save/serialization + * @returns {Object} filtered set of attributes for minimum save/serialization */ export function getCommentAttributes( allAttributes, attributesFromContent ) { // Iterate over attributes and produce the set to save @@ -68,7 +66,7 @@ export function getCommentAttributes( allAttributes, attributesFromContent ) { // save only if attribute if not inferred from the content and if valued return ! ( contentValue !== undefined || allValue === undefined ) - ? Object.assign( toSave, { [ key ]: escapeDoubleQuotes( allValue ) } ) + ? Object.assign( toSave, { [ key ]: allValue } ) : toSave; }, {}, @@ -84,7 +82,7 @@ export function getCommentAttributes( allAttributes, attributesFromContent ) { * @returns {string} stringified equality pair */ function asNameValuePair( value, key ) { - return `${ key }="${ value }`; + return `${ key }="${ escapeDoubleQuotes( value ) }"`; } export function serializeBlock( block ) { From f97ee6afa30af23d3f0dda6b1006c8d45e51b31e Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Thu, 15 Jun 2017 01:30:13 +0200 Subject: [PATCH 08/12] Remove import of `some` --- blocks/api/serializer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js index eacf14f2d1d4e..13d0fb326a1c4 100644 --- a/blocks/api/serializer.js +++ b/blocks/api/serializer.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { isEmpty, map, reduce, some } from 'lodash'; +import { isEmpty, map, reduce } from 'lodash'; import { html as beautifyHtml } from 'js-beautify'; /** From f97431fdfb1ce4c8fb6860d50d77277d264d6bf1 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Thu, 15 Jun 2017 13:35:49 +0200 Subject: [PATCH 09/12] Escape hyphens in block comments to prevent breaking parse --- blocks/api/serializer.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js index 13d0fb326a1c4..c3bfbb0956a80 100644 --- a/blocks/api/serializer.js +++ b/blocks/api/serializer.js @@ -36,9 +36,23 @@ export function getSaveContent( save, attributes ) { return wp.element.renderToString( rawContent ); } -const escapeDoubleQuotes = value => 'string' === typeof value - ? value.replace( /"/g, '\"' ) - : value; +const escapeDoubleQuotes = value => value.replace( /"/g, '\"' ); +const replaceHyphens = value => value.replace( /-/g, '\\-' ); + +/** + * Transform value for storage in block comment + * + * Some special characters and sequences should not + * appear in a block comment header. This transformer + * will guarantee that we store the data safely. + * + * @param {*} value attribute value to serialize + * @returns {*} transformed value + */ +const serializeValue = value => + 'string' === typeof value + ? replaceHyphens( escapeDoubleQuotes( value ) ) + : value; /** * Returns attributes which ought to be saved @@ -82,7 +96,7 @@ export function getCommentAttributes( allAttributes, attributesFromContent ) { * @returns {string} stringified equality pair */ function asNameValuePair( value, key ) { - return `${ key }="${ escapeDoubleQuotes( value ) }"`; + return `${ key }="${ serializeValue( value ) }"`; } export function serializeBlock( block ) { From 05b15a0b3e9c9cf1b95176035a4736c05539a14d Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Thu, 15 Jun 2017 13:50:55 +0200 Subject: [PATCH 10/12] Unescape hyphens in block comment headers --- blocks/api/post.pegjs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/blocks/api/post.pegjs b/blocks/api/post.pegjs index 15bcce0713e8d..6b98d88b41282 100644 --- a/blocks/api/post.pegjs +++ b/blocks/api/post.pegjs @@ -1,8 +1,14 @@ { +function untransformValue( value ) { + return 'string' === typeof value + ? value.replace( /\\-/g, '-' ) + : value; +} + function keyValue( key, value ) { const o = {}; - o[ key ] = value; + o[ key ] = untransformValue( value ); return o; } From 065141e672bb722999d295ecbd24f561a2c763da Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Thu, 15 Jun 2017 14:45:42 +0200 Subject: [PATCH 11/12] Update tests --- blocks/api/serializer.js | 4 +- blocks/api/test/serializer.js | 38 +++++++++++-------- blocks/test/fixtures/core-latestposts.html | 1 - .../fixtures/core-latestposts.serialized.html | 1 - .../core-text-multi-paragraph.serialized.html | 1 - blocks/test/full-content.js | 2 +- 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js index c3bfbb0956a80..bf32bd500a59c 100644 --- a/blocks/api/serializer.js +++ b/blocks/api/serializer.js @@ -49,7 +49,7 @@ const replaceHyphens = value => value.replace( /-/g, '\\-' ); * @param {*} value attribute value to serialize * @returns {*} transformed value */ -const serializeValue = value => +export const serializeValue = value => 'string' === typeof value ? replaceHyphens( escapeDoubleQuotes( value ) ) : value; @@ -110,7 +110,7 @@ export function serializeBlock( block ) { : ''; if ( ! saveContent ) { - return ``; + return ``; } return ( diff --git a/blocks/api/test/serializer.js b/blocks/api/test/serializer.js index e59e15369f65f..ace404fbffdc9 100644 --- a/blocks/api/test/serializer.js +++ b/blocks/api/test/serializer.js @@ -6,7 +6,7 @@ import { expect } from 'chai'; /** * Internal dependencies */ -import serialize, { getCommentAttributes, getSaveContent } from '../serializer'; +import serialize, { getCommentAttributes, getSaveContent, serializeValue } from '../serializer'; import { getBlockTypes, registerBlockType, unregisterBlockType } from '../registration'; describe( 'block serializer', () => { @@ -56,13 +56,13 @@ describe( 'block serializer', () => { } ); describe( 'getCommentAttributes()', () => { - it( 'should return empty string if no difference', () => { + it( 'should return an empty set if no attributes provided', () => { const attributes = getCommentAttributes( {}, {} ); - expect( attributes ).to.equal( '' ); + expect( attributes ).to.eql( {} ); } ); - it( 'should return joined string of key:value pairs by difference subset', () => { + it( 'should only return attributes which cannot be inferred from the content', () => { const attributes = getCommentAttributes( { fruit: 'bananas', category: 'food', @@ -71,25 +71,31 @@ describe( 'block serializer', () => { fruit: 'bananas', } ); - expect( attributes ).to.equal( 'category="food" ripeness="ripe" ' ); + expect( attributes ).to.eql( { + category: 'food', + ripeness: 'ripe', + } ); } ); - it( 'should not append an undefined attribute value', () => { + it( 'should skip attributes whose values are undefined', () => { const attributes = getCommentAttributes( { fruit: 'bananas', - category: 'food', ripeness: undefined, - }, { - fruit: 'bananas', - } ); + }, {} ); + + expect( attributes ).to.eql( { fruit: 'bananas' } ); + } ); + } ); - expect( attributes ).to.equal( 'category="food" ' ); + describe( 'serializeValue()', () => { + it( 'should escape double-quotes', () => { + expect( serializeValue( 'a"b' ) ).to.equal( 'a\"b' ); } ); - it( 'should properly escape attributes with quotes in them', () => { - expect( getCommentAttributes( { - name: 'Kevin "The Yellow Dart" Smith', - }, {} ) ).to.equal( 'name="Kevin \"The Yellow Dart\" Smith" ' ); + it( 'should escape hyphens', () => { + expect( serializeValue( '-' ) ).to.equal( '\u{5c}-' ); + expect( serializeValue( '--' ) ).to.equal( '\u{5c}-\u{5c}-' ); + expect( serializeValue( '\\-' ) ).to.equal( '\u{5c}\u{5c}-' ); } ); } ); @@ -115,7 +121,7 @@ describe( 'block serializer', () => { }, }, ]; - const expectedPostContent = '\n

Ribs & Chicken

\n\n\n'; + const expectedPostContent = '\n

Ribs & Chicken

\n'; expect( serialize( blockList ) ).to.eql( expectedPostContent ); } ); diff --git a/blocks/test/fixtures/core-latestposts.html b/blocks/test/fixtures/core-latestposts.html index 811b2260df738..ee48abd1b4276 100644 --- a/blocks/test/fixtures/core-latestposts.html +++ b/blocks/test/fixtures/core-latestposts.html @@ -1,2 +1 @@ - diff --git a/blocks/test/fixtures/core-latestposts.serialized.html b/blocks/test/fixtures/core-latestposts.serialized.html index 811b2260df738..ee48abd1b4276 100644 --- a/blocks/test/fixtures/core-latestposts.serialized.html +++ b/blocks/test/fixtures/core-latestposts.serialized.html @@ -1,2 +1 @@ - diff --git a/blocks/test/fixtures/core-text-multi-paragraph.serialized.html b/blocks/test/fixtures/core-text-multi-paragraph.serialized.html index e9ab7de13a1cb..d56b01a7c77db 100644 --- a/blocks/test/fixtures/core-text-multi-paragraph.serialized.html +++ b/blocks/test/fixtures/core-text-multi-paragraph.serialized.html @@ -2,4 +2,3 @@

The goal of this new editor is to make adding rich content to WordPress simple and enjoyable. This whole post is composed of pieces of content—somewhat similar to LEGO bricks—that you can move around and interact with. Move your cursor around and you'll notice the different blocks light up with outlines and arrows. Press the arrows to reposition blocks quickly, without fearing about losing things in the process of copying and pasting.

What you are reading now is a text block, the most basic block of all. A text block can have multiple paragraphs, if that's how you prefer to write your posts. But you can also split it by hitting enter twice. Once blocks are split they get their own controls to be moved freely around the post...

- diff --git a/blocks/test/full-content.js b/blocks/test/full-content.js index d4e023d270a06..7a16290ddb872 100644 --- a/blocks/test/full-content.js +++ b/blocks/test/full-content.js @@ -127,7 +127,7 @@ describe( 'full post content fixture', () => { } } - expect( serializedActual ).to.eql( serializedExpected ); + expect( serializedActual.trim() ).to.eql( serializedExpected.trim() ); } ); } ); From 470f1b342e50b944a09beaed052077fdabdae352 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Thu, 15 Jun 2017 14:46:22 +0200 Subject: [PATCH 12/12] Rename replaceHyphens -> escapeHyphens --- blocks/api/serializer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js index bf32bd500a59c..54e64d9ee162b 100644 --- a/blocks/api/serializer.js +++ b/blocks/api/serializer.js @@ -37,7 +37,7 @@ export function getSaveContent( save, attributes ) { } const escapeDoubleQuotes = value => value.replace( /"/g, '\"' ); -const replaceHyphens = value => value.replace( /-/g, '\\-' ); +const escapeHyphens = value => value.replace( /-/g, '\\-' ); /** * Transform value for storage in block comment @@ -51,7 +51,7 @@ const replaceHyphens = value => value.replace( /-/g, '\\-' ); */ export const serializeValue = value => 'string' === typeof value - ? replaceHyphens( escapeDoubleQuotes( value ) ) + ? escapeHyphens( escapeDoubleQuotes( value ) ) : value; /**