From 0380002416d46ec53222ef3399fdd3f5a139c12c Mon Sep 17 00:00:00 2001 From: sujinleeme Date: Thu, 4 Oct 2018 17:37:12 +0900 Subject: [PATCH 01/11] Create ASTPanel component --- js/repl/ASTPanel.js | 29 +++++++++++++++++++++++++++++ js/repl/Repl.js | 14 ++------------ 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/js/repl/ASTPanel.js b/js/repl/ASTPanel.js index e69de29bb2..fac6d89158 100644 --- a/js/repl/ASTPanel.js +++ b/js/repl/ASTPanel.js @@ -0,0 +1,29 @@ +// @flow + +import React from "react"; +import ReactJson from "react-json-view"; + +type Props = { + ast: Object, +}; + +type State = {}; + +export default class ASTPanel extends React.Component { + render() { + const { ast } = this.props; + return ( + field.name !== "root"} + enableClipboard={false} + displayObjectSize={false} + displayDataTypes={false} + /> + ); + } +} diff --git a/js/repl/Repl.js b/js/repl/Repl.js index 01eca7016c..c60b1e7195 100644 --- a/js/repl/Repl.js +++ b/js/repl/Repl.js @@ -35,7 +35,7 @@ import { import WorkerApi from "./WorkerApi"; import scopedEval from "./scopedEval"; import { colors, media } from "./styles"; -import ReactJson from "react-json-view"; +import ASTPanel from "./ASTPanel"; import type { BabelPresets, @@ -248,17 +248,7 @@ class Repl extends React.Component { placeholder="Write code here" /> {state.ast ? ( - field.name !== "root"} - enableClipboard={false} - displayObjectSize={false} - displayDataTypes={false} - /> + ) : ( Date: Fri, 5 Oct 2018 15:51:06 +0900 Subject: [PATCH 02/11] Create flatten, unflatten funcs to control AST source --- js/repl/ASTPanel.js | 91 +++++++++++++++++++++++++++++++------ js/repl/ASTUtils.js | 107 ++++++++++++++++++++++++++++++++++++++++++++ js/repl/Repl.js | 2 +- 3 files changed, 185 insertions(+), 15 deletions(-) create mode 100644 js/repl/ASTUtils.js diff --git a/js/repl/ASTPanel.js b/js/repl/ASTPanel.js index fac6d89158..e931e83ce5 100644 --- a/js/repl/ASTPanel.js +++ b/js/repl/ASTPanel.js @@ -2,28 +2,91 @@ import React from "react"; import ReactJson from "react-json-view"; +import { flatten, unflatten, sortedKeys } from "./ASTUtils"; type Props = { - ast: Object, + src: Object, }; -type State = {}; +type State = { + src: Object, + flattenSrc: Object, + deleteSrc: Object, + astOption: { + autofocus: boolean, + location: boolean, + empty: boolean, + type: boolean, + }, +}; + +const OPTION_ORDER = ["autofocus", "location", "empty", "type"]; export default class ASTPanel extends React.Component { + state = { + src: {}, + flattenSrc: {}, + deleteSrc: {}, + astOption: { + autofocus: true, + location: true, + empty: true, + type: true, + }, + }; + + static getDerivedStateFromProps(nextProps: Props, prevState: State) { + const flattenSrc = flatten(nextProps.src); + if (nextProps.src !== prevState.src) { + return { + src: unflatten(flattenSrc), + flattenSrc: flattenSrc, + }; + } + return null; + } + + onOptionSettingCheck(option: string) { + this.setState(prevState => ({ + astOption: { + ...prevState.astOption, + [option]: !prevState.astOption[option], + }, + })); + } + render() { - const { ast } = this.props; + const { src, astOption } = this.state; + return ( - field.name !== "root"} - enableClipboard={false} - displayObjectSize={false} - displayDataTypes={false} - /> +
+ {OPTION_ORDER.map(option => { + return ( + + ); + })} + {src && ( + field.name !== "root"} + enableClipboard={false} + displayObjectSize={false} + displayDataTypes={false} + /> + )} +
); } } diff --git a/js/repl/ASTUtils.js b/js/repl/ASTUtils.js new file mode 100644 index 0000000000..920a5a1396 --- /dev/null +++ b/js/repl/ASTUtils.js @@ -0,0 +1,107 @@ +// https://github.com/hughsk/flat/blob/master/index.js + +function isBuffer(obj) { + return ( + obj != null && + obj.constructor != null && + typeof obj.constructor.isBuffer === "function" && + obj.constructor.isBuffer(obj) + ); +} + +function flatten(target, opts) { + opts = opts || {}; + + const delimiter = opts.delimiter || "."; + const maxDepth = opts.maxDepth; + const output = {}; + + function step(object, prev, currentDepth) { + currentDepth = currentDepth || 1; + Object.keys(object).forEach(function(key) { + const value = object[key]; + const isarray = opts.safe && Array.isArray(value); + const type = Object.prototype.toString.call(value); + const isbuffer = isBuffer(value); + const isobject = type === "[object Object]" || type === "[object Array]"; + const newKey = prev ? prev + delimiter + key : key; + if ( + !isarray && + !isbuffer && + isobject && + Object.keys(value).length && + (!opts.maxDepth || currentDepth < maxDepth) + ) { + return step(value, newKey, currentDepth + 1); + } + output[newKey] = value; + }); + } + + step(target); + + return output; +} + +function unflatten(target, opts) { + opts = opts || {}; + + const delimiter = opts.delimiter || "."; + const overwrite = opts.overwrite || false; + const result = {}; + const isbuffer = isBuffer(target); + if ( + isbuffer || + Object.prototype.toString.call(target) !== "[object Object]" + ) { + return target; + } + + // safely ensure that the key is + // an integer. + function getkey(key) { + const parsedKey = Number(key); + + return isNaN(parsedKey) || key.indexOf(".") !== -1 || opts.object + ? key + : parsedKey; + } + + const sortedKeys = Object.keys(target).sort(function(keyA, keyB) { + return keyA.length - keyB.length; + }); + + sortedKeys.forEach(function(key) { + const split = key.split(delimiter); + let key1 = getkey(split.shift()); + let key2 = getkey(split[0]); + let recipient = result; + + while (key2 !== undefined) { + const type = Object.prototype.toString.call(recipient[key1]); + const isobject = type === "[object Object]" || type === "[object Array]"; + + // do not write over falsey, non-undefined values if overwrite is false + if (!overwrite && !isobject && typeof recipient[key1] !== "undefined") { + return; + } + + if ((overwrite && !isobject) || (!overwrite && recipient[key1] == null)) { + recipient[key1] = typeof key2 === "number" && !opts.object ? [] : {}; + } + + recipient = recipient[key1]; + if (split.length > 0) { + key1 = getkey(split.shift()); + key2 = getkey(split[0]); + } + } + + // unflatten again for 'messy objects' + recipient[key1] = unflatten(target[key], opts); + }); + + return result; +} + +export { flatten, unflatten }; diff --git a/js/repl/Repl.js b/js/repl/Repl.js index c60b1e7195..2de3ba0f3c 100644 --- a/js/repl/Repl.js +++ b/js/repl/Repl.js @@ -248,7 +248,7 @@ class Repl extends React.Component { placeholder="Write code here" /> {state.ast ? ( - + ) : ( Date: Fri, 5 Oct 2018 19:31:21 +0900 Subject: [PATCH 03/11] Create deleteFlatten, mergeFlatten funcs --- js/repl/ASTUtils.js | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/js/repl/ASTUtils.js b/js/repl/ASTUtils.js index 920a5a1396..babfa068c7 100644 --- a/js/repl/ASTUtils.js +++ b/js/repl/ASTUtils.js @@ -104,4 +104,33 @@ function unflatten(target, opts) { return result; } -export { flatten, unflatten }; +function filterFlatten(flattenSrc, type) { + const result = Object.keys(flattenSrc) + .filter(key => { + const keys = key.split("."); + return keys.includes(type); + }) + .reduce((object, key) => { + object[key] = flattenSrc[key]; + return object; + }, {}); + return result; +} + +function deleteFlatten(currentSrc, deletedSrc) { + const deletedKeys = Object.keys(deletedSrc); + const result = Object.keys(currentSrc).reduce((object, key) => { + !deletedKeys.includes(key) ? (object[key] = currentSrc[key]) : null; + return object; + }, {}); + return result; +} + +function mergeFlatten(currentSrc, nextSrc) { + return { + ...currentSrc, + ...nextSrc, + }; +} + +export { flatten, unflatten, filterFlatten, deleteFlatten, mergeFlatten }; From c97537944b6e74842079057f01d937b5c96c6fb1 Mon Sep 17 00:00:00 2001 From: sujinleeme Date: Fri, 5 Oct 2018 19:33:18 +0900 Subject: [PATCH 04/11] Trigger show/hide type keys in AST ouput object --- js/repl/ASTPanel.js | 109 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 92 insertions(+), 17 deletions(-) diff --git a/js/repl/ASTPanel.js b/js/repl/ASTPanel.js index e931e83ce5..8fa08acf03 100644 --- a/js/repl/ASTPanel.js +++ b/js/repl/ASTPanel.js @@ -1,17 +1,26 @@ // @flow +import { css } from "emotion"; +import { colors } from "./styles"; import React from "react"; import ReactJson from "react-json-view"; -import { flatten, unflatten, sortedKeys } from "./ASTUtils"; +import { + flatten, + unflatten, + filterFlatten, + deleteFlatten, + mergeFlatten, +} from "./ASTUtils"; type Props = { + className?: string, src: Object, }; type State = { src: Object, flattenSrc: Object, - deleteSrc: Object, + flattenType: Object, astOption: { autofocus: boolean, location: boolean, @@ -26,7 +35,7 @@ export default class ASTPanel extends React.Component { state = { src: {}, flattenSrc: {}, - deleteSrc: {}, + flattenType: {}, astOption: { autofocus: true, location: true, @@ -41,6 +50,7 @@ export default class ASTPanel extends React.Component { return { src: unflatten(flattenSrc), flattenSrc: flattenSrc, + flattenType: filterFlatten(flattenSrc, "type"), }; } return null; @@ -53,26 +63,60 @@ export default class ASTPanel extends React.Component { [option]: !prevState.astOption[option], }, })); + + this.onChangeJson(option); + } + + onChangeJson(option: string) { + const { src, astOption, flattenSrc, flattenType } = this.state; + // const OPTION_ORDER = ["autofocus", "location", "empty", "type"]; + + function triggerAstOutput(type) { + const isShow = astOption[type]; + let newSrc = {}; + const types = { + autofocus: () => {}, + empty: () => {}, + type: () => { + if (isShow) { + newSrc = deleteFlatten(flattenSrc, flattenType); + } else { + newSrc = mergeFlatten(flattenSrc, flattenType); + } + return newSrc; + }, + location: () => { + console.log(isShow, type); + }, + default: () => {}, + }; + return (types[type] || types["default"])(); + } + + const result = triggerAstOutput(option); + this.setState({ flattenSrc: result, src: unflatten(result) }); } render() { const { src, astOption } = this.state; + const { className = "" } = this.props; return ( -
- {OPTION_ORDER.map(option => { - return ( - - ); - })} +
+
+ {OPTION_ORDER.map(option => { + return ( + + ); + })} +
{src && ( { ); } } + +const styles = { + codeMirror: css({ + display: "block", + height: "100%", + width: "100%", + overflow: "auto", + position: "relative", + }), + // error: css({ + // order: 2, + // backgroundColor: colors.errorBackground, + // borderTop: `1px solid ${colors.errorBorder}`, + // color: colors.errorForeground, + // ...sharedBoxStyles, + // }), + // info: css({ + // order: 1, + // backgroundColor: colors.infoBackground, + // borderTop: `1px solid ${colors.infoBorder}`, + // color: colors.infoForeground, + // ...sharedBoxStyles, + // }), + panel: css({ + height: "100%", + display: "flex", + flexDirection: "column", + justifyContent: "stretch", + overflow: "auto", + }), +}; From 2d8aba19a0d4d706f413b9194b72005a32cc6a7b Mon Sep 17 00:00:00 2001 From: sujinleeme Date: Sat, 6 Oct 2018 16:10:49 +0900 Subject: [PATCH 05/11] code review by @benevbright --- js/repl/ASTPanel.js | 48 ++++++++++++++++++++++----------------------- js/repl/ASTUtils.js | 4 ---- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/js/repl/ASTPanel.js b/js/repl/ASTPanel.js index 8fa08acf03..497210bd12 100644 --- a/js/repl/ASTPanel.js +++ b/js/repl/ASTPanel.js @@ -45,8 +45,8 @@ export default class ASTPanel extends React.Component { }; static getDerivedStateFromProps(nextProps: Props, prevState: State) { - const flattenSrc = flatten(nextProps.src); if (nextProps.src !== prevState.src) { + const flattenSrc = flatten(nextProps.src); return { src: unflatten(flattenSrc), flattenSrc: flattenSrc, @@ -56,20 +56,22 @@ export default class ASTPanel extends React.Component { return null; } - onOptionSettingCheck(option: string) { - this.setState(prevState => ({ - astOption: { - ...prevState.astOption, - [option]: !prevState.astOption[option], - }, - })); - - this.onChangeJson(option); + _onOptionSettingCheck(option: string) { + this.setState( + prevState => ( + { + astOption: { + ...prevState.astOption, + [option]: !prevState.astOption[option], + }, + }, + this._onChangeJson(option) + ) + ); } - onChangeJson(option: string) { + _onChangeJson(option: string) { const { src, astOption, flattenSrc, flattenType } = this.state; - // const OPTION_ORDER = ["autofocus", "location", "empty", "type"]; function triggerAstOutput(type) { const isShow = astOption[type]; @@ -104,18 +106,16 @@ export default class ASTPanel extends React.Component { return (
- {OPTION_ORDER.map(option => { - return ( - - ); - })} + {OPTION_ORDER.map(option => ( + + ))}
{src && ( Date: Sun, 7 Oct 2018 01:21:35 +0900 Subject: [PATCH 06/11] fix popup error msg while ast code is compiling --- js/repl/ASTPanel.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/js/repl/ASTPanel.js b/js/repl/ASTPanel.js index 497210bd12..45cade464d 100644 --- a/js/repl/ASTPanel.js +++ b/js/repl/ASTPanel.js @@ -45,13 +45,15 @@ export default class ASTPanel extends React.Component { }; static getDerivedStateFromProps(nextProps: Props, prevState: State) { - if (nextProps.src !== prevState.src) { - const flattenSrc = flatten(nextProps.src); - return { - src: unflatten(flattenSrc), - flattenSrc: flattenSrc, - flattenType: filterFlatten(flattenSrc, "type"), - }; + if (nextProps.src) { + if (nextProps.src !== prevState.src) { + const flattenSrc = flatten(nextProps.src); + return { + src: unflatten(flattenSrc), + flattenSrc: flattenSrc, + flattenType: filterFlatten(flattenSrc, "type"), + }; + } } return null; } From 3c6e1a6b0d5cb49b262c2d3546d9b795324bfb63 Mon Sep 17 00:00:00 2001 From: sujinleeme Date: Sun, 7 Oct 2018 03:11:26 +0900 Subject: [PATCH 07/11] filter location, null value in AST options --- js/repl/ASTPanel.js | 62 ++++++++++++++++++++++++++++----------------- js/repl/ASTUtils.js | 12 ++++++--- 2 files changed, 48 insertions(+), 26 deletions(-) diff --git a/js/repl/ASTPanel.js b/js/repl/ASTPanel.js index 45cade464d..ba210cbc21 100644 --- a/js/repl/ASTPanel.js +++ b/js/repl/ASTPanel.js @@ -19,8 +19,10 @@ type Props = { type State = { src: Object, + flattenEmpty: Object, flattenSrc: Object, flattenType: Object, + flattenLocation: Object, astOption: { autofocus: boolean, location: boolean, @@ -34,8 +36,10 @@ const OPTION_ORDER = ["autofocus", "location", "empty", "type"]; export default class ASTPanel extends React.Component { state = { src: {}, + flattenEmpty: {}, flattenSrc: {}, flattenType: {}, + flattenLocation: {}, astOption: { autofocus: true, location: true, @@ -49,48 +53,60 @@ export default class ASTPanel extends React.Component { if (nextProps.src !== prevState.src) { const flattenSrc = flatten(nextProps.src); return { - src: unflatten(flattenSrc), + src: nextProps.src, flattenSrc: flattenSrc, flattenType: filterFlatten(flattenSrc, "type"), + flattenLocation: { + ...filterFlatten(flattenSrc, "start"), + ...filterFlatten(flattenSrc, "end"), + }, + flattenEmpty: filterFlatten(flattenSrc, null, "null"), }; } } - return null; } _onOptionSettingCheck(option: string) { - this.setState( - prevState => ( - { - astOption: { - ...prevState.astOption, - [option]: !prevState.astOption[option], - }, - }, - this._onChangeJson(option) - ) - ); + this.setState(prevState => ({ + astOption: { + ...prevState.astOption, + [option]: !prevState.astOption[option], + }, + })); + this._onChangeJson(option); } _onChangeJson(option: string) { - const { src, astOption, flattenSrc, flattenType } = this.state; + const { + astOption, + flattenEmpty, + flattenSrc, + flattenType, + flattenLocation, + } = this.state; function triggerAstOutput(type) { const isShow = astOption[type]; let newSrc = {}; const types = { autofocus: () => {}, - empty: () => {}, + empty: () => { + newSrc = isShow + ? deleteFlatten(flattenSrc, flattenEmpty) + : mergeFlatten(flattenSrc, flattenEmpty); + return newSrc; + }, type: () => { - if (isShow) { - newSrc = deleteFlatten(flattenSrc, flattenType); - } else { - newSrc = mergeFlatten(flattenSrc, flattenType); - } + newSrc = isShow + ? deleteFlatten(flattenSrc, flattenType) + : mergeFlatten(flattenSrc, flattenType); return newSrc; }, location: () => { - console.log(isShow, type); + newSrc = isShow + ? deleteFlatten(flattenSrc, flattenLocation) + : mergeFlatten(flattenSrc, flattenLocation); + return newSrc; }, default: () => {}, }; @@ -126,9 +142,9 @@ export default class ASTPanel extends React.Component { overflowY: "scroll", width: "100%", }} - shouldCollapse={field => field.name !== "root"} + sortKeys={true} enableClipboard={false} - displayObjectSize={false} + displayObjectSize={true} displayDataTypes={false} /> )} diff --git a/js/repl/ASTUtils.js b/js/repl/ASTUtils.js index 5050543d0a..0751359a22 100644 --- a/js/repl/ASTUtils.js +++ b/js/repl/ASTUtils.js @@ -100,11 +100,17 @@ function unflatten(target, opts) { return result; } -function filterFlatten(flattenSrc, type) { +function filterFlatten(flattenSrc, type = "", value) { const result = Object.keys(flattenSrc) .filter(key => { - const keys = key.split("."); - return keys.includes(type); + if (type) { + const keys = key.split("."); + return keys.includes(type); + } + if (value) { + const v = value === "null" || value === "undefined" ? null : value; + return flattenSrc[key] === v; + } }) .reduce((object, key) => { object[key] = flattenSrc[key]; From 5f236b6fab0fcefcbf374248969d82eb9062fe6f Mon Sep 17 00:00:00 2001 From: sujinleeme Date: Mon, 8 Oct 2018 15:05:09 +0900 Subject: [PATCH 08/11] if-else shorthand in getDerivedStateFromProps --- js/repl/ASTPanel.js | 80 +++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/js/repl/ASTPanel.js b/js/repl/ASTPanel.js index ba210cbc21..06705c49fb 100644 --- a/js/repl/ASTPanel.js +++ b/js/repl/ASTPanel.js @@ -49,20 +49,18 @@ export default class ASTPanel extends React.Component { }; static getDerivedStateFromProps(nextProps: Props, prevState: State) { - if (nextProps.src) { - if (nextProps.src !== prevState.src) { - const flattenSrc = flatten(nextProps.src); - return { - src: nextProps.src, - flattenSrc: flattenSrc, - flattenType: filterFlatten(flattenSrc, "type"), - flattenLocation: { - ...filterFlatten(flattenSrc, "start"), - ...filterFlatten(flattenSrc, "end"), - }, - flattenEmpty: filterFlatten(flattenSrc, null, "null"), - }; - } + if (nextProps.src && nextProps.src !== prevState.src) { + const flattenSrc = flatten(nextProps.src); + return { + src: nextProps.src, + flattenSrc: flattenSrc, + flattenType: filterFlatten(flattenSrc, "type"), + flattenLocation: { + ...filterFlatten(flattenSrc, "start"), + ...filterFlatten(flattenSrc, "end"), + }, + flattenEmpty: filterFlatten(flattenSrc, null, "null"), + }; } } @@ -123,12 +121,13 @@ export default class ASTPanel extends React.Component { return (
-
+
{OPTION_ORDER.map(option => ( -