diff --git a/README.md b/README.md index 6b21ac9f5..993fdbbdc 100644 --- a/README.md +++ b/README.md @@ -84,18 +84,24 @@ console.log(reactElementToJSXString(
Hello, world!
)); If false, default props are omitted unless they differ from from the default value. -**options.showFunctions: boolean, default false** +**options.showFunctions: boolean | (fn: Function, prop: string) => boolean, default false** If true, functions bodies are shown. If false, functions bodies are replaced with `function noRefCheck() {}`. -**options.functionValue: function, default `(fn) => fn`** + If a function is passed, it will be called for each function prop with its value and key, and will not print bodies of functions that return false. + +**options.functionValue: (fn: Function, prop: string) => Function | string, default undefined** Allows you to override the default formatting of function values. `functionValue` receives the original function reference as input - and should send any value as output. + and should return a new function or formatted string. + + Pre-defined inline and multi-line formatters are exported as `inlineFunction` and `preserveFunctionLineBreak` respectively. + + The default formatting depends on context: multi-line for function children and inline for props. **options.tabStop: number, default 2** diff --git a/src/formatter/formatComplexDataStructure.js b/src/formatter/formatComplexDataStructure.js index 57abb6136..b6f6b6149 100644 --- a/src/formatter/formatComplexDataStructure.js +++ b/src/formatter/formatComplexDataStructure.js @@ -11,6 +11,7 @@ import type { Options } from './../options'; export default ( value: Object | Array, + name: string, inline: boolean, lvl: number, options: Options @@ -31,7 +32,7 @@ export default ( } if (typeof currentValue === 'function') { - return formatFunction(currentValue, options); + return formatFunction(currentValue, name, true, lvl, options); } return originalResult; diff --git a/src/formatter/formatComplexDataStructure.spec.js b/src/formatter/formatComplexDataStructure.spec.js index 81ff66f6f..5b1c17197 100644 --- a/src/formatter/formatComplexDataStructure.spec.js +++ b/src/formatter/formatComplexDataStructure.spec.js @@ -15,7 +15,9 @@ describe('formatComplexDataStructure', () => { it('should format an object', () => { const fixture = { a: 1, b: { c: 'ccc' } }; - expect(formatComplexDataStructure(fixture, false, 0, options)).toEqual( + expect( + formatComplexDataStructure(fixture, 'foo', false, 0, options) + ).toEqual( `{ a: 1, b: { @@ -28,19 +30,23 @@ describe('formatComplexDataStructure', () => { it('should format inline an object', () => { const fixture = { a: 1, b: { c: 'ccc' } }; - expect(formatComplexDataStructure(fixture, true, 0, options)).toEqual( - "{a: 1, b: {c: 'ccc'}}" - ); + expect( + formatComplexDataStructure(fixture, 'foo', true, 0, options) + ).toEqual("{a: 1, b: {c: 'ccc'}}"); }); it('should format an empty object', () => { - expect(formatComplexDataStructure({}, false, 0, options)).toEqual('{}'); + expect(formatComplexDataStructure({}, 'foo', false, 0, options)).toEqual( + '{}' + ); }); it('should order the object keys', () => { const fixture = { b: { d: 'ddd', c: 'ccc' }, a: 1 }; - expect(formatComplexDataStructure(fixture, false, 0, options)).toEqual( + expect( + formatComplexDataStructure(fixture, 'foo', false, 0, options) + ).toEqual( `{ a: 1, b: { @@ -54,7 +60,9 @@ describe('formatComplexDataStructure', () => { it('should format an array', () => { const fixture = [1, '2', true, false, null]; - expect(formatComplexDataStructure(fixture, false, 0, options)).toEqual( + expect( + formatComplexDataStructure(fixture, 'foo', false, 0, options) + ).toEqual( `[ 1, '2', @@ -68,15 +76,17 @@ describe('formatComplexDataStructure', () => { it('should format inline an array ', () => { const fixture = [1, '2', true, false, null]; - expect(formatComplexDataStructure(fixture, true, 0, options)).toEqual( - "[1, '2', true, false, null]" - ); + expect( + formatComplexDataStructure(fixture, 'foo', true, 0, options) + ).toEqual("[1, '2', true, false, null]"); }); it('should format an object that contains a react element', () => { const fixture = { a: createFakeReactElement('BarBar') }; - expect(formatComplexDataStructure(fixture, false, 0, options)).toEqual( + expect( + formatComplexDataStructure(fixture, 'foo', false, 0, options) + ).toEqual( `{ a: }` @@ -84,23 +94,25 @@ describe('formatComplexDataStructure', () => { }); it('should format an empty array', () => { - expect(formatComplexDataStructure([], false, 0, options)).toEqual('[]'); + expect(formatComplexDataStructure([], 'foo', false, 0, options)).toEqual( + '[]' + ); }); it('should format an object that contains a date', () => { const fixture = { a: new Date('2017-11-13T00:00:00.000Z') }; - expect(formatComplexDataStructure(fixture, true, 0, options)).toEqual( - `{a: new Date('2017-11-13T00:00:00.000Z')}` - ); + expect( + formatComplexDataStructure(fixture, 'foo', true, 0, options) + ).toEqual(`{a: new Date('2017-11-13T00:00:00.000Z')}`); }); it('should format an object that contains a regexp', () => { const fixture = { a: /test/g }; - expect(formatComplexDataStructure(fixture, true, 0, options)).toEqual( - `{a: /test/g}` - ); + expect( + formatComplexDataStructure(fixture, 'foo', true, 0, options) + ).toEqual(`{a: /test/g}`); }); it('should replace a function with noRefCheck', () => { @@ -110,9 +122,9 @@ describe('formatComplexDataStructure', () => { }, }; - expect(formatComplexDataStructure(fixture, true, 0, options)).toEqual( - '{a: function noRefCheck() {}}' - ); + expect( + formatComplexDataStructure(fixture, 'foo', true, 0, options) + ).toEqual('{a: function noRefCheck() {}}'); }); it('should format a function', () => { @@ -123,7 +135,7 @@ describe('formatComplexDataStructure', () => { }; expect( - formatComplexDataStructure(fixture, true, 0, { + formatComplexDataStructure(fixture, 'foo', true, 0, { ...options, showFunctions: true, }) @@ -138,7 +150,7 @@ describe('formatComplexDataStructure', () => { }; expect( - formatComplexDataStructure(fixture, true, 0, { + formatComplexDataStructure(fixture, 'foo', true, 0, { ...options, functionValue: () => '', }) diff --git a/src/formatter/formatFunction.js b/src/formatter/formatFunction.js index f8474436b..94bae7122 100644 --- a/src/formatter/formatFunction.js +++ b/src/formatter/formatFunction.js @@ -1,4 +1,5 @@ import type { Options } from './../options'; +import spacer from './spacer'; function noRefCheck() {} @@ -11,13 +12,26 @@ export const inlineFunction = (fn: any): string => export const preserveFunctionLineBreak = (fn: any): string => fn.toString(); -const defaultFunctionValue = inlineFunction; +export default ( + fn: Function, + prop: string, + inline: boolean, + lvl: number, + options: Options +): string => { + const { functionValue, showFunctions } = options; + const functionFn = + functionValue || (inline ? inlineFunction : preserveFunctionLineBreak); + const shouldShowFunction = Boolean( + functionValue || + (typeof showFunctions === 'function' + ? showFunctions(fn, prop) + : showFunctions) + ); -export default (fn: Function, options: Options): string => { - const { functionValue = defaultFunctionValue, showFunctions } = options; - if (!showFunctions && functionValue === defaultFunctionValue) { - return functionValue(noRefCheck); - } - - return functionValue(fn); + return String(functionFn(shouldShowFunction ? fn : noRefCheck, prop)) + .split('\n') + .map(ln => spacer(lvl, options.tabStop) + ln) + .join('\n') + .trim(); }; diff --git a/src/formatter/formatFunction.spec.js b/src/formatter/formatFunction.spec.js index 8a199696b..9f844fadc 100644 --- a/src/formatter/formatFunction.spec.js +++ b/src/formatter/formatFunction.spec.js @@ -12,36 +12,40 @@ function hello() { describe('formatFunction', () => { it('should replace a function with noRefCheck without showFunctions option', () => { - expect(formatFunction(hello, {})).toEqual('function noRefCheck() {}'); + expect(formatFunction(hello, 'prop', true, 0, {})).toEqual( + 'function noRefCheck() {}' + ); }); it('should replace a function with noRefCheck if showFunctions is false', () => { - expect(formatFunction(hello, { showFunctions: false })).toEqual( - 'function noRefCheck() {}' - ); + expect( + formatFunction(hello, 'prop', true, 0, { showFunctions: false }) + ).toEqual('function noRefCheck() {}'); }); it('should format a function if showFunctions is true', () => { - expect(formatFunction(hello, { showFunctions: true })).toEqual( - 'function hello() {return 1;}' - ); + expect( + formatFunction(hello, 'prop', true, 0, { showFunctions: true }) + ).toEqual('function hello() {return 1;}'); }); it('should format a function without name if showFunctions is true', () => { - expect(formatFunction(() => 1, { showFunctions: true })).toEqual( - 'function () {return 1;}' - ); + expect( + formatFunction(() => 1, 'prop', true, 0, { showFunctions: true }) + ).toEqual('function () {return 1;}'); }); it('should use the functionValue option', () => { - expect(formatFunction(hello, { functionValue: () => '' })).toEqual( - '' - ); + expect( + formatFunction(hello, 'prop', true, 0, { + functionValue: () => '', + }) + ).toEqual(''); }); it('should use the functionValue option even if showFunctions is true', () => { expect( - formatFunction(hello, { + formatFunction(hello, 'prop', true, 0, { showFunctions: true, functionValue: () => '', }) @@ -50,10 +54,28 @@ describe('formatFunction', () => { it('should use the functionValue option even if showFunctions is false', () => { expect( - formatFunction(hello, { + formatFunction(hello, 'prop', true, 0, { showFunctions: false, functionValue: () => '', }) ).toEqual(''); }); + + it('should format multi-line function', () => { + expect( + formatFunction(hello, 'prop', false, 0, { + showFunctions: true, + tabStop: 2, + }) + ).toEqual('function hello() {\n return 1;\n}'); + }); + + it('should format multi-line function with indentation', () => { + expect( + formatFunction(hello, 'prop', false, 1, { + showFunctions: true, + tabStop: 2, + }) + ).toEqual('function hello() {\n return 1;\n }'); + }); }); diff --git a/src/formatter/formatProp.js b/src/formatter/formatProp.js index 9fcac3c01..bdecf17be 100644 --- a/src/formatter/formatProp.js +++ b/src/formatter/formatProp.js @@ -28,7 +28,13 @@ export default ( const { useBooleanShorthandSyntax, tabStop } = options; - const formattedPropValue = formatPropValue(usedValue, inline, lvl, options); + const formattedPropValue = formatPropValue( + usedValue, + name, + inline, + lvl, + options + ); let attributeFormattedInline = ' '; let attributeFormattedMultiline = `\n${spacer(lvl + 1, tabStop)}`; diff --git a/src/formatter/formatProp.spec.js b/src/formatter/formatProp.spec.js index 700c8fa9b..b25490194 100644 --- a/src/formatter/formatProp.spec.js +++ b/src/formatter/formatProp.spec.js @@ -30,6 +30,7 @@ describe('formatProp', () => { expect(formatPropValue).toHaveBeenCalledWith( 'bar', + 'foo', true, 0, defaultOptions @@ -50,6 +51,7 @@ describe('formatProp', () => { expect(formatPropValue).toHaveBeenCalledWith( 'baz', + 'foo', true, 0, defaultOptions @@ -70,6 +72,7 @@ describe('formatProp', () => { expect(formatPropValue).toHaveBeenCalledWith( 'bar', + 'foo', true, 0, defaultOptions @@ -93,7 +96,7 @@ describe('formatProp', () => { isMultilineAttribute: false, }); - expect(formatPropValue).toHaveBeenCalledWith(true, true, 0, options); + expect(formatPropValue).toHaveBeenCalledWith(true, 'foo', true, 0, options); }); it('should ignore a falsy boolean prop (with short syntax)', () => { @@ -112,7 +115,13 @@ describe('formatProp', () => { isMultilineAttribute: false, }); - expect(formatPropValue).toHaveBeenCalledWith(false, true, 0, options); + expect(formatPropValue).toHaveBeenCalledWith( + false, + 'foo', + true, + 0, + options + ); }); it('should format a truthy boolean prop (with explicit syntax)', () => { @@ -132,7 +141,7 @@ describe('formatProp', () => { isMultilineAttribute: false, }); - expect(formatPropValue).toHaveBeenCalledWith(true, true, 0, options); + expect(formatPropValue).toHaveBeenCalledWith(true, 'foo', true, 0, options); }); it('should format a falsy boolean prop (with explicit syntax)', () => { @@ -152,7 +161,13 @@ describe('formatProp', () => { isMultilineAttribute: false, }); - expect(formatPropValue).toHaveBeenCalledWith(false, true, 0, options); + expect(formatPropValue).toHaveBeenCalledWith( + false, + 'foo', + true, + 0, + options + ); }); it('should format a mulitline props', () => { @@ -187,6 +202,7 @@ describe('formatProp', () => { expect(formatPropValue).toHaveBeenCalledWith( ['a', 'b'], + 'foo', false, 0, defaultOptions @@ -217,6 +233,12 @@ describe('formatProp', () => { isMultilineAttribute: false, }); - expect(formatPropValue).toHaveBeenCalledWith('bar', true, 4, options); + expect(formatPropValue).toHaveBeenCalledWith( + 'bar', + 'foo', + true, + 4, + options + ); }); }); diff --git a/src/formatter/formatPropValue.js b/src/formatter/formatPropValue.js index 1044e3b37..76a167ea7 100644 --- a/src/formatter/formatPropValue.js +++ b/src/formatter/formatPropValue.js @@ -12,6 +12,7 @@ const escape = (s: string): string => s.replace(/"/g, '"'); const formatPropValue = ( propValue: any, + propName: string, inline: boolean, lvl: number, options: Options @@ -41,7 +42,7 @@ const formatPropValue = ( } if (typeof propValue === 'function') { - return `{${formatFunction(propValue, options)}}`; + return `{${formatFunction(propValue, propName, true, lvl, options)}}`; } if (isValidElement(propValue)) { @@ -61,7 +62,13 @@ const formatPropValue = ( } if (isPlainObject(propValue) || Array.isArray(propValue)) { - return `{${formatComplexDataStructure(propValue, inline, lvl, options)}}`; + return `{${formatComplexDataStructure( + propValue, + propName, + inline, + lvl, + options + )}}`; } return `{${String(propValue)}}`; diff --git a/src/formatter/formatPropValue.spec.js b/src/formatter/formatPropValue.spec.js index 8d0a0eeb4..2443dbff8 100644 --- a/src/formatter/formatPropValue.spec.js +++ b/src/formatter/formatPropValue.spec.js @@ -20,28 +20,28 @@ describe('formatPropValue', () => { }); it('should format an integer prop value', () => { - expect(formatPropValue(42, false, 0, {})).toBe('{42}'); + expect(formatPropValue(42, 'foo', false, 0, {})).toBe('{42}'); }); it('should escape double quote on prop value of string type', () => { - expect(formatPropValue('Hello "Jonh"!', false, 0, {})).toBe( + expect(formatPropValue('Hello "Jonh"!', 'foo', false, 0, {})).toBe( '"Hello "Jonh"!"' ); }); it('should format a symbol prop value', () => { - expect(formatPropValue(Symbol('Foo'), false, 0, {})).toBe( + expect(formatPropValue(Symbol('Foo'), 'foo', false, 0, {})).toBe( "{Symbol('Foo')}" ); // eslint-disable-next-line symbol-description - expect(formatPropValue(Symbol(), false, 0, {})).toBe('{Symbol()}'); + expect(formatPropValue(Symbol(), 'foo', false, 0, {})).toBe('{Symbol()}'); }); it('should replace a function prop value by a an empty generic function by default', () => { const doThings = a => a * 2; - expect(formatPropValue(doThings, false, 0, {})).toBe( + expect(formatPropValue(doThings, 'foo', false, 0, {})).toBe( '{function noRefCheck() {}}' ); }); @@ -49,9 +49,9 @@ describe('formatPropValue', () => { it('should show the function prop value implementation if "showFunctions" option is true', () => { const doThings = a => a * 2; - expect(formatPropValue(doThings, false, 0, { showFunctions: true })).toBe( - '{function doThings(a) {return a * 2;}}' - ); + expect( + formatPropValue(doThings, 'foo', false, 0, { showFunctions: true }) + ).toBe('{function doThings(a) {return a * 2;}}'); }); it('should format the function prop value with the "functionValue" option', () => { @@ -64,14 +64,14 @@ describe('formatPropValue', () => { }; expect( - formatPropValue(doThings, false, 0, { + formatPropValue(doThings, 'foo', false, 0, { functionValue, showFunctions: true, }) ).toBe('{function Myfunction() {}}'); expect( - formatPropValue(doThings, false, 0, { + formatPropValue(doThings, 'foo', false, 0, { functionValue, showFunctions: false, }) @@ -79,7 +79,7 @@ describe('formatPropValue', () => { }); it('should parse and format a react element prop value', () => { - expect(formatPropValue(
, false, 0, {})).toBe( + expect(formatPropValue(
, 'foo', false, 0, {})).toBe( '{}' ); @@ -89,18 +89,18 @@ describe('formatPropValue', () => { it('should format a date prop value', () => { expect( - formatPropValue(new Date('2017-01-01T11:00:00.000Z'), false, 0, {}) + formatPropValue(new Date('2017-01-01T11:00:00.000Z'), 'foo', false, 0, {}) ).toBe('{new Date("2017-01-01T11:00:00.000Z")}'); }); it('should format an invalid date prop value', () => { - expect(formatPropValue(new Date(NaN), false, 0, {})).toBe( + expect(formatPropValue(new Date(NaN), 'foo', false, 0, {})).toBe( '{new Date(NaN)}' ); }); it('should format an object prop value', () => { - expect(formatPropValue({ foo: 42 }, false, 0, {})).toBe( + expect(formatPropValue({ foo: 42 }, 'foo', false, 0, {})).toBe( '{*Mocked formatComplexDataStructure result*}' ); @@ -108,7 +108,7 @@ describe('formatPropValue', () => { }); it('should format an array prop value', () => { - expect(formatPropValue(['a', 'b', 'c'], false, 0, {})).toBe( + expect(formatPropValue(['a', 'b', 'c'], 'foo', false, 0, {})).toBe( '{*Mocked formatComplexDataStructure result*}' ); @@ -116,23 +116,25 @@ describe('formatPropValue', () => { }); it('should format a boolean prop value', () => { - expect(formatPropValue(true, false, 0, {})).toBe('{true}'); - expect(formatPropValue(false, false, 0, {})).toBe('{false}'); + expect(formatPropValue(true, 'foo', false, 0, {})).toBe('{true}'); + expect(formatPropValue(false, 'foo', false, 0, {})).toBe('{false}'); }); it('should format null prop value', () => { - expect(formatPropValue(null, false, 0, {})).toBe('{null}'); + expect(formatPropValue(null, 'foo', false, 0, {})).toBe('{null}'); }); it('should format undefined prop value', () => { - expect(formatPropValue(undefined, false, 0, {})).toBe('{undefined}'); + expect(formatPropValue(undefined, 'foo', false, 0, {})).toBe('{undefined}'); }); it('should call the ".toString()" method on object instance prop value', () => { - expect(formatPropValue(new Set(['a', 'b', 42]), false, 0, {})).toBe( + expect(formatPropValue(new Set(['a', 'b', 42]), 'foo', false, 0, {})).toBe( '{[object Set]}' ); - expect(formatPropValue(new Map(), false, 0, {})).toBe('{[object Map]}'); + expect(formatPropValue(new Map(), 'foo', false, 0, {})).toBe( + '{[object Map]}' + ); }); }); diff --git a/src/formatter/formatTreeNode.js b/src/formatter/formatTreeNode.js index 0d5ea3429..432d5041e 100644 --- a/src/formatter/formatTreeNode.js +++ b/src/formatter/formatTreeNode.js @@ -2,6 +2,7 @@ import formatReactElementNode from './formatReactElementNode'; import formatReactFragmentNode from './formatReactFragmentNode'; +import formatFunction from './formatFunction'; import type { Options } from './../options'; import type { TreeNode } from './../tree'; @@ -46,6 +47,10 @@ export default ( : ''; } + if (node.type === 'function') { + return `{${formatFunction(node.value, 'children', inline, lvl, options)}}`; + } + if (node.type === 'ReactElement') { return formatReactElementNode(node, inline, lvl, options); } diff --git a/src/formatter/formatTreeNode.spec.js b/src/formatter/formatTreeNode.spec.js index a3d172e51..bbe03543a 100644 --- a/src/formatter/formatTreeNode.spec.js +++ b/src/formatter/formatTreeNode.spec.js @@ -19,6 +19,25 @@ describe('formatTreeNode', () => { ); }); + it('should format function tree node', () => { + function fun(a) { + return a + 1; + } + expect( + formatTreeNode( + { + type: 'function', + value: fun, + }, + true, + 0, + { + showFunctions: true, + } + ) + ).toBe('{function fun(a) {return a + 1;}}'); + }); + it('should format react element tree node', () => { expect( formatTreeNode( diff --git a/src/index.spec.js b/src/index.spec.js index c2b4c288c..4be512c3d 100644 --- a/src/index.spec.js +++ b/src/index.spec.js @@ -903,6 +903,29 @@ describe('reactElementToJSXString(ReactElement)', () => { ).toEqual(`
`); }); + it('should ident deeply nested multi-line functions correctly', () => { + /* eslint-disable arrow-body-style */ + const fn = () => { + return 'value'; + }; + + expect( + reactElementToJSXString( +
+
+
+
+
, + { + showFunctions: true, + functionValue: preserveFunctionLineBreak, + } + ) + ).toEqual( + "\n \n \n
\n
" + ); + }); + it('should expose the multiline "functionValue" formatter', () => { /* eslint-disable arrow-body-style */ const fn = () => { @@ -1113,6 +1136,17 @@ describe('reactElementToJSXString(ReactElement)', () => { ).toEqual(`
} />`); }); + it('reactElementToJSXString(
{() =>
}
)', () => { + const renderJSX = jsx => + reactElementToJSXString(jsx, { + showFunctions: (_, prop) => prop === 'children', + functionValue: fn => `() => ${renderJSX(fn())}`, + }); + expect(renderJSX(
{() =>
}
)).toEqual( + `
\n {() =>
}\n
` + ); + }); + it('should not cause recursive loop when prop object contains an element', () => { const Test = () =>
Test
; diff --git a/src/options.js b/src/options.js index ccb4a331f..38d6c27fe 100644 --- a/src/options.js +++ b/src/options.js @@ -5,8 +5,8 @@ import * as React from 'react'; export type Options = {| filterProps: string[], showDefaultProps: boolean, - showFunctions: boolean, - functionValue: Function, + showFunctions: boolean | ((fn: Function, prop: string) => boolean), + functionValue: (fn: Function, prop: string) => Function | string, tabStop: number, useBooleanShorthandSyntax: boolean, useFragmentShortSyntax: boolean, diff --git a/src/parser/parseReactElement.js b/src/parser/parseReactElement.js index 773b1a98d..1b7c75063 100644 --- a/src/parser/parseReactElement.js +++ b/src/parser/parseReactElement.js @@ -5,6 +5,7 @@ import type { Options } from './../options'; import { createStringTreeNode, createNumberTreeNode, + createFunctionTreeNode, createReactElementTreeNode, createReactFragmentTreeNode, } from './../tree'; @@ -67,9 +68,15 @@ const parseReactElement = ( } const defaultProps = filterProps(element.type.defaultProps || {}, noChildren); - const childrens = React.Children.toArray(element.props.children) - .filter(onlyMeaningfulChildren) - .map(child => parseReactElement(child, options)); + + let childrens; + if (typeof element.props.children === 'function') { + childrens = [createFunctionTreeNode(element.props.children)]; + } else { + childrens = React.Children.toArray(element.props.children) + .filter(onlyMeaningfulChildren) + .map(child => parseReactElement(child, options)); + } 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..a911b2dea 100644 --- a/src/parser/parseReactElement.spec.js +++ b/src/parser/parseReactElement.spec.js @@ -182,4 +182,25 @@ describe('parseReactElement', () => { ], }); }); + + it('should parse children function', () => { + const RenderProp = ({ children }) => children({}); + const fun = () =>
; + expect( + parseReactElement({fun}, options) + ).toEqual({ + type: 'ReactElement', + displayName: 'RenderProp', + defaultProps: {}, + props: { + key: 'foo', + }, + childrens: [ + { + type: 'function', + value: fun, + }, + ], + }); + }); }); diff --git a/src/tree.js b/src/tree.js index efbf254af..f7e936fd9 100644 --- a/src/tree.js +++ b/src/tree.js @@ -16,6 +16,11 @@ export type NumberTreeNode = {| value: number, |}; +export type FunctionTreeNode = {| + type: 'function', + value: Function, +|}; + export type ReactElementTreeNode = {| type: 'ReactElement', displayName: string, @@ -33,6 +38,7 @@ export type ReactFragmentTreeNode = {| export type TreeNode = | StringTreeNode | NumberTreeNode + | FunctionTreeNode | ReactElementTreeNode | ReactFragmentTreeNode; @@ -46,6 +52,11 @@ export const createNumberTreeNode = (value: number): NumberTreeNode => ({ value, }); +export const createFunctionTreeNode = (value: Function): FunctionTreeNode => ({ + type: 'function', + value, +}); + export const createReactElementTreeNode = ( displayName: string, props: PropsType, diff --git a/src/tree.spec.js b/src/tree.spec.js index febf01823..c6ba7a2ad 100644 --- a/src/tree.spec.js +++ b/src/tree.spec.js @@ -3,6 +3,7 @@ import { createStringTreeNode, createNumberTreeNode, + createFunctionTreeNode, createReactElementTreeNode, createReactFragmentTreeNode, } from './tree'; @@ -25,6 +26,16 @@ describe('createNumberTreeNode', () => { }); }); +describe('createFunctionTreeNode', () => { + it('generate a number typed node payload', () => { + const fun = () => null; + expect(createFunctionTreeNode(fun)).toEqual({ + type: 'function', + value: fun, + }); + }); +}); + describe('createReactElementTreeNode', () => { it('generate a react element typed node payload', () => { expect(