From 1dde89eaa5ce691c39ed3cfe7471670cba0ec582 Mon Sep 17 00:00:00 2001 From: Adam Mockor Date: Fri, 4 Jan 2019 10:00:29 +0100 Subject: [PATCH 1/3] feat(formatting): render children as function --- src/formatter/formatReactFunctionNode.js | 32 ++++++++++++++++++++++++ src/formatter/formatTreeNode.js | 5 ++++ src/index.spec.js | 16 ++++++++++++ src/parser/parseReactElement.js | 10 ++++++++ src/parser/parseReactElement.spec.js | 23 +++++++++++++++++ src/tree.js | 12 +++++++++ 6 files changed, 98 insertions(+) create mode 100644 src/formatter/formatReactFunctionNode.js diff --git a/src/formatter/formatReactFunctionNode.js b/src/formatter/formatReactFunctionNode.js new file mode 100644 index 000000000..67acf1b3b --- /dev/null +++ b/src/formatter/formatReactFunctionNode.js @@ -0,0 +1,32 @@ +/* @flow */ + +import spacer from './spacer'; +import formatTreeNode from './formatTreeNode'; +import type { Options } from './../options'; +import type { ReactFunctionTreeNode } from './../tree'; + +export default ( + node: ReactFunctionTreeNode, + inline: boolean, + lvl: number, + options: Options +): string => { + const { tabStop } = options; + const { type, childrens } = node; + + if (type !== 'ReactFunction') { + throw new Error( + `The "formatReactFunctionNode" function could only format node of type "ReactFunction". Given: ${ + type + }` + ); + } + + const functionRender = formatTreeNode(childrens, false, lvl + 1, options); + + const out = `{() => ( +${spacer(lvl + 1, tabStop)}${functionRender} +${spacer(lvl, tabStop)})}`; + + return `${out}`; +}; diff --git a/src/formatter/formatTreeNode.js b/src/formatter/formatTreeNode.js index 0a24d7909..b8ea40758 100644 --- a/src/formatter/formatTreeNode.js +++ b/src/formatter/formatTreeNode.js @@ -2,6 +2,7 @@ import formatReactElementNode from './formatReactElementNode'; import formatReactFragmentNode from './formatReactFragmentNode'; +import formatReactFunctionNode from './formatReactFunctionNode'; import type { Options } from './../options'; import type { TreeNode } from './../tree'; @@ -54,5 +55,9 @@ export default ( return formatReactFragmentNode(node, inline, lvl, options); } + if (node.type === 'ReactFunction') { + return formatReactFunctionNode(node, inline, lvl, options); + } + throw new TypeError(`Unknow format type "${node.type}"`); }; diff --git a/src/index.spec.js b/src/index.spec.js index 23b57bb81..09430d656 100644 --- a/src/index.spec.js +++ b/src/index.spec.js @@ -897,6 +897,22 @@ describe('reactElementToJSXString(ReactElement)', () => { ); }); + it('reactElementToJSXString(
{() => (
Hello World
)}
)', () => { + expect( + reactElementToJSXString(
{() =>
Hello World
}
, { + showFunctions: true, + }) + ).toEqual( + `
+ {() => ( +
+ Hello World +
+ )} +
` + ); + }); + it('reactElementToJSXString()', () => { expect(reactElementToJSXString()).toEqual( '' diff --git a/src/parser/parseReactElement.js b/src/parser/parseReactElement.js index 7b991bd65..a4f9cfe76 100644 --- a/src/parser/parseReactElement.js +++ b/src/parser/parseReactElement.js @@ -6,6 +6,7 @@ import { createStringTreeNode, createNumberTreeNode, createReactElementTreeNode, + createReactFunctionTreeNode, createReactFragmentTreeNode, } from './../tree'; import type { TreeNode } from './../tree'; @@ -71,6 +72,15 @@ const parseReactElement = ( .filter(onlyMeaningfulChildren) .map(child => parseReactElement(child, options)); + if (typeof element.props.children === 'function') { + const functionChildrens = parseReactElement( + element.props.children(), + options, + true + ); + childrens.push(createReactFunctionTreeNode(functionChildrens)); + } + if (supportFragment && element.type === Fragment) { return createReactFragmentTreeNode(key, childrens); } diff --git a/src/parser/parseReactElement.spec.js b/src/parser/parseReactElement.spec.js index a30188849..7267a34e8 100644 --- a/src/parser/parseReactElement.spec.js +++ b/src/parser/parseReactElement.spec.js @@ -21,6 +21,29 @@ describe('parseReactElement', () => { }); }); + it('should parse a react element with a function as children', () => { + expect( + parseReactElement(

{() =>
hello world
}

, options) + ).toEqual({ + childrens: [ + { + childrens: { + childrens: [{ type: 'string', value: 'hello world' }], + defaultProps: {}, + displayName: 'div', + props: {}, + type: 'ReactElement', + }, + type: 'ReactFunction', + }, + ], + defaultProps: {}, + displayName: 'h1', + props: {}, + type: 'ReactElement', + }); + }); + it('should filter empty childrens', () => { expect( parseReactElement( diff --git a/src/tree.js b/src/tree.js index efbf254af..8138e6d9f 100644 --- a/src/tree.js +++ b/src/tree.js @@ -16,6 +16,11 @@ export type NumberTreeNode = {| value: number, |}; +export type ReactFunctionTreeNode = {| + type: 'ReactFunction', + childrens: TreeNode[], +|}; + export type ReactElementTreeNode = {| type: 'ReactElement', displayName: string, @@ -46,6 +51,13 @@ export const createNumberTreeNode = (value: number): NumberTreeNode => ({ value, }); +export const createReactFunctionTreeNode = ( + childrens: TreeNode[] +): ReactFunctionTreeNode => ({ + type: 'ReactFunction', + childrens, +}); + export const createReactElementTreeNode = ( displayName: string, props: PropsType, From 677d5cfe608baea31d2a1ff0889595686151cacd Mon Sep 17 00:00:00 2001 From: Adam Mockor Date: Fri, 4 Jan 2019 12:46:27 +0100 Subject: [PATCH 2/3] fix(formatting): fix how formatReactFunctionNode handles arrays --- src/formatter/formatReactFunctionNode.js | 6 +++++- src/parser/parseReactElement.js | 5 ++--- src/parser/parseReactElement.spec.js | 16 +++++++++------- src/tree.js | 3 ++- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/formatter/formatReactFunctionNode.js b/src/formatter/formatReactFunctionNode.js index 67acf1b3b..bb3939983 100644 --- a/src/formatter/formatReactFunctionNode.js +++ b/src/formatter/formatReactFunctionNode.js @@ -22,7 +22,11 @@ export default ( ); } - const functionRender = formatTreeNode(childrens, false, lvl + 1, options); + const functionRender = Array.isArray(childrens) + ? childrens + .map(children => formatTreeNode(children, false, lvl + 1, options)) + .join('\n') + : formatTreeNode(childrens, false, lvl + 1, options); const out = `{() => ( ${spacer(lvl + 1, tabStop)}${functionRender} diff --git a/src/parser/parseReactElement.js b/src/parser/parseReactElement.js index a4f9cfe76..152be1d58 100644 --- a/src/parser/parseReactElement.js +++ b/src/parser/parseReactElement.js @@ -75,10 +75,9 @@ const parseReactElement = ( if (typeof element.props.children === 'function') { const functionChildrens = parseReactElement( element.props.children(), - options, - true + options ); - childrens.push(createReactFunctionTreeNode(functionChildrens)); + childrens.push(createReactFunctionTreeNode([functionChildrens])); } if (supportFragment && element.type === Fragment) { diff --git a/src/parser/parseReactElement.spec.js b/src/parser/parseReactElement.spec.js index 7267a34e8..329554b15 100644 --- a/src/parser/parseReactElement.spec.js +++ b/src/parser/parseReactElement.spec.js @@ -27,13 +27,15 @@ describe('parseReactElement', () => { ).toEqual({ childrens: [ { - childrens: { - childrens: [{ type: 'string', value: 'hello world' }], - defaultProps: {}, - displayName: 'div', - props: {}, - type: 'ReactElement', - }, + childrens: [ + { + childrens: [{ type: 'string', value: 'hello world' }], + defaultProps: {}, + displayName: 'div', + props: {}, + type: 'ReactElement', + }, + ], type: 'ReactFunction', }, ], diff --git a/src/tree.js b/src/tree.js index 8138e6d9f..a4c12e5ee 100644 --- a/src/tree.js +++ b/src/tree.js @@ -39,7 +39,8 @@ export type TreeNode = | StringTreeNode | NumberTreeNode | ReactElementTreeNode - | ReactFragmentTreeNode; + | ReactFragmentTreeNode + | ReactFunctionTreeNode; export const createStringTreeNode = (value: string): StringTreeNode => ({ type: 'string', From b97837c8cc83693001c31a13ae3b08025cb6d4c8 Mon Sep 17 00:00:00 2001 From: Adam Mockor Date: Sun, 6 Jan 2019 09:43:10 +0100 Subject: [PATCH 3/3] fix: prettier formating --- src/formatter/formatReactFunctionNode.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/formatter/formatReactFunctionNode.js b/src/formatter/formatReactFunctionNode.js index bb3939983..2a4bbbbd4 100644 --- a/src/formatter/formatReactFunctionNode.js +++ b/src/formatter/formatReactFunctionNode.js @@ -16,9 +16,7 @@ export default ( if (type !== 'ReactFunction') { throw new Error( - `The "formatReactFunctionNode" function could only format node of type "ReactFunction". Given: ${ - type - }` + `The "formatReactFunctionNode" function could only format node of type "ReactFunction". Given: ${type}` ); }