From 4b3b4a41d0a634bc4f2fabf9f6715bb1d273ff03 Mon Sep 17 00:00:00 2001
From: Ben Conolly <ben@thinkmill.com.au>
Date: Fri, 12 Apr 2019 13:14:37 +1000
Subject: [PATCH] Add logical expression operator (#61)

---
 .changeset/4298a628/changes.json              |   7 +
 .changeset/4298a628/changes.md                |   1 +
 .../__snapshots__/test.js.snap                | 133 ++++++++++++++++++
 packages/extract-react-types/src/index.js     |   9 ++
 packages/extract-react-types/src/kinds.js     |  11 +-
 packages/extract-react-types/test.js          |  35 +++++
 packages/kind2string/src/index.js             |   4 +-
 packages/kind2string/test.js                  |  13 ++
 8 files changed, 211 insertions(+), 2 deletions(-)
 create mode 100644 .changeset/4298a628/changes.json
 create mode 100644 .changeset/4298a628/changes.md

diff --git a/.changeset/4298a628/changes.json b/.changeset/4298a628/changes.json
new file mode 100644
index 00000000..42a4dcd5
--- /dev/null
+++ b/.changeset/4298a628/changes.json
@@ -0,0 +1,7 @@
+{
+  "releases": [
+    { "name": "extract-react-types", "type": "patch" },
+    { "name": "kind2string", "type": "patch" }
+  ],
+  "dependents": []
+}
diff --git a/.changeset/4298a628/changes.md b/.changeset/4298a628/changes.md
new file mode 100644
index 00000000..fa228725
--- /dev/null
+++ b/.changeset/4298a628/changes.md
@@ -0,0 +1 @@
+- Add logicalExpression converter
\ No newline at end of file
diff --git a/packages/extract-react-types/__snapshots__/test.js.snap b/packages/extract-react-types/__snapshots__/test.js.snap
index 9ea1e2d9..e3ee917a 100644
--- a/packages/extract-react-types/__snapshots__/test.js.snap
+++ b/packages/extract-react-types/__snapshots__/test.js.snap
@@ -208,6 +208,139 @@ Object {
 }
 `;
 
+exports[`LogicalExpression and 1`] = `
+Object {
+  "component": Object {
+    "kind": "object",
+    "members": Array [
+      Object {
+        "default": Object {
+          "kind": "logicalExpression",
+          "left": Object {
+            "kind": "boolean",
+            "value": true,
+          },
+          "operator": "&&",
+          "right": Object {
+            "kind": "string",
+            "value": "something",
+          },
+        },
+        "key": Object {
+          "kind": "id",
+          "name": "and",
+        },
+        "kind": "property",
+        "optional": false,
+        "value": Object {
+          "kind": "string",
+        },
+      },
+    ],
+    "name": Object {
+      "kind": "id",
+      "name": "Button",
+      "type": null,
+    },
+  },
+  "kind": "program",
+}
+`;
+
+exports[`LogicalExpression or 1`] = `
+Object {
+  "component": Object {
+    "kind": "object",
+    "members": Array [
+      Object {
+        "default": Object {
+          "kind": "logicalExpression",
+          "left": Object {
+            "kind": "string",
+            "value": "me",
+          },
+          "operator": "||",
+          "right": Object {
+            "kind": "string",
+            "value": "you",
+          },
+        },
+        "key": Object {
+          "kind": "id",
+          "name": "or",
+        },
+        "kind": "property",
+        "optional": false,
+        "value": Object {
+          "kind": "string",
+        },
+      },
+    ],
+    "name": Object {
+      "kind": "id",
+      "name": "Button",
+      "type": null,
+    },
+  },
+  "kind": "program",
+}
+`;
+
+exports[`LogicalExpression or complicated 1`] = `
+Object {
+  "component": Object {
+    "kind": "object",
+    "members": Array [
+      Object {
+        "default": Object {
+          "kind": "logicalExpression",
+          "left": Object {
+            "kind": "logicalExpression",
+            "left": Object {
+              "kind": "string",
+              "value": "me",
+            },
+            "operator": "||",
+            "right": Object {
+              "kind": "string",
+              "value": "you",
+            },
+          },
+          "operator": "||",
+          "right": Object {
+            "kind": "logicalExpression",
+            "left": Object {
+              "kind": "string",
+              "value": "someone else",
+            },
+            "operator": "&&",
+            "right": Object {
+              "kind": "string",
+              "value": "impossible state",
+            },
+          },
+        },
+        "key": Object {
+          "kind": "id",
+          "name": "or",
+        },
+        "kind": "property",
+        "optional": false,
+        "value": Object {
+          "kind": "string",
+        },
+      },
+    ],
+    "name": Object {
+      "kind": "id",
+      "name": "Button",
+      "type": null,
+    },
+  },
+  "kind": "program",
+}
+`;
+
 exports[`NullLiteralTypeAnnotation 1`] = `
 Object {
   "component": Object {
diff --git a/packages/extract-react-types/src/index.js b/packages/extract-react-types/src/index.js
index edcd99f4..6dfdaeca 100644
--- a/packages/extract-react-types/src/index.js
+++ b/packages/extract-react-types/src/index.js
@@ -248,6 +248,15 @@ converters.TemplateLiteral = (path, context) /*: K.TemplateLiteral*/ => {
   };
 };
 
+converters.LogicalExpression = (path, context) => {
+  return {
+    kind: 'logicalExpression',
+    operator: path.node.operator,
+    left: convert(path.get('left'), context),
+    right: convert(path.get('right'), context),
+  };
+};
+
 converters.RestElement = (path, context) /*: K.Rest */ => {
   return {
     kind: 'rest',
diff --git a/packages/extract-react-types/src/kinds.js b/packages/extract-react-types/src/kinds.js
index 49c50ce9..3c8372ab 100644
--- a/packages/extract-react-types/src/kinds.js
+++ b/packages/extract-react-types/src/kinds.js
@@ -42,6 +42,13 @@ export type ArrayType = {
   type: AnyTypeKind,
 }
 
+export type LogicalExpression = {
+  kind: "logicalExpression",
+  operator: string,
+  left: AnyValueKind,
+  right: AnyValueKind
+};
+
 export type Obj = { kind: "object", members: Array<Property> };
 export type Property = {
   kind: "property",
@@ -211,6 +218,8 @@ export type AnyValueKind =
   | String
   | TemplateExpression
   | TemplateLiteral
-  | Variable;
+  | Variable
+  | LogicalExpression;
+
 export type AnyKind = AnyTypeKind | AnyValueKind | Program;
 */
diff --git a/packages/extract-react-types/test.js b/packages/extract-react-types/test.js
index 285bd9f1..4930c2eb 100644
--- a/packages/extract-react-types/test.js
+++ b/packages/extract-react-types/test.js
@@ -25,6 +25,41 @@ const TESTS = [
     }
   `
   },
+  {
+    name: 'LogicalExpression and',
+    typeSystem: 'flow',
+    code: `
+    class Button extends React.Component<{ and: string }> {
+      static defaultProps = {
+        and: true && 'something',
+      }
+    }
+    
+  `
+  },
+  {
+    name: 'LogicalExpression or',
+    typeSystem: 'flow',
+    code: `
+    class Button extends React.Component<{ or: string }> {
+      static defaultProps = {
+        or: 'me' || 'you',
+      }
+    }
+    
+  `
+  },
+  {
+    name: 'LogicalExpression or complicated',
+    typeSystem: 'flow',
+    code: `
+    class Button extends React.Component<{ or: string }> {
+      static defaultProps = {
+        or: 'me' || 'you' || 'someone else' && 'impossible state',
+      }
+    }
+  `
+  },
   {
     name: 'flow string',
     typeSystem: 'flow',
diff --git a/packages/kind2string/src/index.js b/packages/kind2string/src/index.js
index 51e6ca32..c8583bef 100644
--- a/packages/kind2string/src/index.js
+++ b/packages/kind2string/src/index.js
@@ -67,7 +67,9 @@ const converters = {
   literal: (type /*: any */, mode /*: string */) /*: string*/ => `${type.kind}`,
   mixed: (type /*: K.Mixed*/, mode /*: string */) /*:string*/ => type.kind,
   null: (type /*: K.Null */, mode /*: string */) /*: 'null' */ => 'null',
-
+  logicalExpression: (type, mode /*: string */) /*:string*/ => {
+    return `${convert(type.left)} ${type.operator} ${convert(type.right)}`;
+  },
   unary: (type /*: K.Unary*/, mode /*: string */) /*:string*/ => {
     let space = unaryWhiteList.includes(type.operator) ? '' : ' ';
     return `${type.operator}${space}${convert(type.argument)}`;
diff --git a/packages/kind2string/test.js b/packages/kind2string/test.js
index 8b4005d1..1ba321ba 100644
--- a/packages/kind2string/test.js
+++ b/packages/kind2string/test.js
@@ -350,6 +350,19 @@ describe('kind 2 string tests', () => {
     let converted = convert(res);
     expect(converted).toBe('ascii');
   });
+  describe('logicalExpression', () => {
+    it('should work', () => {
+      let file = `class Button extends React.Component<{ or: string }> {
+        static defaultProps = {
+          or: 'me' || 'you' || 'someone else' && 'impossible state',
+        }
+      }`;
+
+      let res = extractReactTypes(file, 'flow').component.members[0].default;
+      let converted = convert(res);
+      expect(converted).toBe('"me" || "you" || "someone else" && "impossible state"');
+    });
+  });
   describe('utilities', () => {
     describe('resolveLast', () => {});
   });