webui: generate TS types for graphql queries

Quentin Gliech created

Change summary

webui/codegen.yaml             |  23 +
webui/package-lock.json        | 592 +++++++++++++++++++++++++++++------
webui/package.json             |   7 
webui/src/.gitignore           |   2 
webui/src/Author.graphql       |   8 
webui/src/Author.js            |  12 
webui/src/Label.graphql        |   8 
webui/src/Label.js             |  12 
webui/src/bug/Bug.graphql      |  14 
webui/src/bug/Bug.tsx          |  26 -
webui/src/bug/BugQuery.graphql |   9 
webui/src/bug/BugQuery.js      |  30 -
webui/src/bug/BugQuery.tsx     |  20 +
13 files changed, 578 insertions(+), 185 deletions(-)

Detailed changes

webui/codegen.yaml 🔗

@@ -1,8 +1,31 @@
 schema: '../graphql/schema/*.graphql'
 overwrite: true
+documents: src/**/*.graphql
 generates:
   ./src/fragmentTypes.js:
     plugins:
     - fragment-matcher
     config:
       module: es2015
+  ./src/gqlTypes.ts:
+    plugins:
+    - typescript
+  ./src/:
+    plugins:
+    - typescript-operations
+    - typescript-react-apollo
+    preset: near-operation-file
+    presetConfig:
+      extension: .generated.tsx
+      baseTypesPath: gqlTypes.ts
+    config:
+      withComponent: false
+      withHOC: false
+      withHooks: true
+
+config:
+  documentMode: documentNode
+
+hooks:
+  afterOneFileWrite:
+  - prettier --write

webui/package-lock.json 🔗

@@ -598,6 +598,15 @@
         "@babel/helper-plugin-utils": "^7.8.0"
       }
     },
+    "@babel/plugin-syntax-class-properties": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.8.3.tgz",
+      "integrity": "sha512-UcAyQWg2bAN647Q+O811tG9MrJ38Z10jjhQdKNAL8fsyPzE3cCN/uT+f55cFVY4aGO4jqJAvmqsuY3GQDwAoXg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.3"
+      }
+    },
     "@babel/plugin-syntax-decorators": {
       "version": "7.8.3",
       "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.8.3.tgz",
@@ -1219,6 +1228,16 @@
         "tslib": "^1"
       }
     },
+    "@graphql-codegen/add": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@graphql-codegen/add/-/add-1.12.1.tgz",
+      "integrity": "sha512-i6+Al5+Z8WH4eIF4Nzsu2imXN1hLNPt+91v0Bm4n4XIOi3mbLtbEo8IxK354mOpriie1PCpUJq7Y9dofPONObA==",
+      "dev": true,
+      "requires": {
+        "@graphql-codegen/plugin-helpers": "1.12.1",
+        "tslib": "1.10.0"
+      }
+    },
     "@graphql-codegen/cli": {
       "version": "1.12.1",
       "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-1.12.1.tgz",
@@ -1296,6 +1315,18 @@
         "@graphql-codegen/plugin-helpers": "1.12.1"
       }
     },
+    "@graphql-codegen/near-operation-file-preset": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@graphql-codegen/near-operation-file-preset/-/near-operation-file-preset-1.12.1.tgz",
+      "integrity": "sha512-916+QqcUsnJOOgtRP4m7JDLnfwrQWufsRjdlDZL58pwrhNU0sRYObSyDH8RYhA8XIt5k29P+s2ZwHkGDRzGR1g==",
+      "dev": true,
+      "requires": {
+        "@graphql-codegen/add": "1.12.1",
+        "@graphql-codegen/plugin-helpers": "1.12.1",
+        "@graphql-codegen/visitor-plugin-common": "1.12.1",
+        "tslib": "1.10.0"
+      }
+    },
     "@graphql-codegen/plugin-helpers": {
       "version": "1.12.1",
       "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-1.12.1.tgz",
@@ -1314,6 +1345,60 @@
         "upper-case": "2.0.1"
       }
     },
+    "@graphql-codegen/typescript": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-1.12.1.tgz",
+      "integrity": "sha512-j2iQrxSIMxF6MlzJ5OvnawRyewfEHlC21It+Z6Md2z7yJK5T2bYLs4jd0RkzDGVezodnpd0TzV/Yv+jGxdMjSQ==",
+      "dev": true,
+      "requires": {
+        "@graphql-codegen/plugin-helpers": "1.12.1",
+        "@graphql-codegen/visitor-plugin-common": "1.12.1",
+        "auto-bind": "4.0.0",
+        "tslib": "1.10.0"
+      }
+    },
+    "@graphql-codegen/typescript-operations": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-1.12.1.tgz",
+      "integrity": "sha512-lGF5wzi5Rndca4YoQCTczRamBrKkieuLIMW3/WT+t3s9wwTkC802HAuNLmUW9CUO+lcArrXV23qpyi9hElKZAA==",
+      "dev": true,
+      "requires": {
+        "@graphql-codegen/plugin-helpers": "1.12.1",
+        "@graphql-codegen/typescript": "1.12.1",
+        "@graphql-codegen/visitor-plugin-common": "1.12.1",
+        "auto-bind": "4.0.0",
+        "tslib": "1.10.0"
+      }
+    },
+    "@graphql-codegen/typescript-react-apollo": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-1.12.1.tgz",
+      "integrity": "sha512-uy6R2M6EvLXu9BWA9untZBpE+64OUnC2zjV0TO2cK6tz1Ktut9pyC7F4BNGoFHfTvmx80dxtKfwbqdmP48mddQ==",
+      "dev": true,
+      "requires": {
+        "@graphql-codegen/plugin-helpers": "1.12.1",
+        "@graphql-codegen/visitor-plugin-common": "1.12.1",
+        "auto-bind": "4.0.0",
+        "camel-case": "4.1.1",
+        "pascal-case": "3.1.1",
+        "tslib": "1.10.0"
+      }
+    },
+    "@graphql-codegen/visitor-plugin-common": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.12.1.tgz",
+      "integrity": "sha512-dfcb0d1u0bGo7KgaGoWMUrFec9rKB5JiiFatHfs2CK6h6iT6giqkuyHy8247tH3yvjk+xFoIG2NVIqg9jUyv+w==",
+      "dev": true,
+      "requires": {
+        "@graphql-codegen/plugin-helpers": "1.12.1",
+        "@graphql-toolkit/relay-operation-optimizer": "0.9.7",
+        "auto-bind": "4.0.0",
+        "dependency-graph": "0.8.1",
+        "graphql-tag": "2.10.1",
+        "pascal-case": "3.1.1",
+        "tslib": "1.10.0"
+      }
+    },
     "@graphql-toolkit/apollo-engine-loader": {
       "version": "0.9.7",
       "resolved": "https://registry.npmjs.org/@graphql-toolkit/apollo-engine-loader/-/apollo-engine-loader-0.9.7.tgz",
@@ -1443,6 +1528,16 @@
         "tslib": "1.10.0"
       }
     },
+    "@graphql-toolkit/relay-operation-optimizer": {
+      "version": "0.9.7",
+      "resolved": "https://registry.npmjs.org/@graphql-toolkit/relay-operation-optimizer/-/relay-operation-optimizer-0.9.7.tgz",
+      "integrity": "sha512-IPFAbKMOX3RdjyDuamK9ziuTFD5tsCiTVvHACHA2wgg+32krJZJsV6STKhFLqIwytS40vt5zhZydQCFxIrCD5g==",
+      "dev": true,
+      "requires": {
+        "@graphql-toolkit/common": "0.9.7",
+        "relay-compiler": "8.0.0"
+      }
+    },
     "@graphql-toolkit/schema-merging": {
       "version": "0.9.7",
       "resolved": "https://registry.npmjs.org/@graphql-toolkit/schema-merging/-/schema-merging-0.9.7.tgz",
@@ -2287,8 +2382,7 @@
     "@types/history": {
       "version": "4.7.4",
       "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.4.tgz",
-      "integrity": "sha512-+o2igcuZA3xtOoFH56s+MCZVidwlJNcJID57DSCyawS2i910yG9vkwehCjJNZ6ImhCR5S9DbvIJKyYHcMyOfMw==",
-      "dev": true
+      "integrity": "sha512-+o2igcuZA3xtOoFH56s+MCZVidwlJNcJID57DSCyawS2i910yG9vkwehCjJNZ6ImhCR5S9DbvIJKyYHcMyOfMw=="
     },
     "@types/is-glob": {
       "version": "4.0.1",
@@ -2318,112 +2412,6 @@
         "@types/istanbul-lib-report": "*"
       }
     },
-    "@types/jest": {
-      "version": "25.1.1",
-      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.1.1.tgz",
-      "integrity": "sha512-bKSZJYZJLzwaoVYNN4W3A0RvKNYsrLm5tsuXaMlfYDxKf4gY2sFrMYneCugNQWGg1gjPW+FHBwNrwPzEi4sIsw==",
-      "requires": {
-        "jest-diff": "^25.1.0",
-        "pretty-format": "^25.1.0"
-      },
-      "dependencies": {
-        "@jest/types": {
-          "version": "25.1.0",
-          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz",
-          "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==",
-          "requires": {
-            "@types/istanbul-lib-coverage": "^2.0.0",
-            "@types/istanbul-reports": "^1.1.1",
-            "@types/yargs": "^15.0.0",
-            "chalk": "^3.0.0"
-          }
-        },
-        "@types/yargs": {
-          "version": "15.0.2",
-          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.2.tgz",
-          "integrity": "sha512-hFkuAp58M2xOc1QgJhkFrLMnqa8KWTFRTnzrI1zlEcOfg3DZ0eH3aPAo/N6QlVVu8E4KS4xD1jtEG3rdQYFmIg==",
-          "requires": {
-            "@types/yargs-parser": "*"
-          }
-        },
-        "ansi-styles": {
-          "version": "4.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
-          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
-          "requires": {
-            "@types/color-name": "^1.1.1",
-            "color-convert": "^2.0.1"
-          }
-        },
-        "chalk": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
-          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
-          "requires": {
-            "ansi-styles": "^4.1.0",
-            "supports-color": "^7.1.0"
-          }
-        },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-          "requires": {
-            "color-name": "~1.1.4"
-          }
-        },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
-        },
-        "diff-sequences": {
-          "version": "25.1.0",
-          "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.1.0.tgz",
-          "integrity": "sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw=="
-        },
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
-        },
-        "jest-diff": {
-          "version": "25.1.0",
-          "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.1.0.tgz",
-          "integrity": "sha512-nepXgajT+h017APJTreSieh4zCqnSHEJ1iT8HDlewu630lSJ4Kjjr9KNzm+kzGwwcpsDE6Snx1GJGzzsefaEHw==",
-          "requires": {
-            "chalk": "^3.0.0",
-            "diff-sequences": "^25.1.0",
-            "jest-get-type": "^25.1.0",
-            "pretty-format": "^25.1.0"
-          }
-        },
-        "jest-get-type": {
-          "version": "25.1.0",
-          "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz",
-          "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw=="
-        },
-        "pretty-format": {
-          "version": "25.1.0",
-          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz",
-          "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==",
-          "requires": {
-            "@jest/types": "^25.1.0",
-            "ansi-regex": "^5.0.0",
-            "ansi-styles": "^4.0.0",
-            "react-is": "^16.12.0"
-          }
-        },
-        "supports-color": {
-          "version": "7.1.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
-          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
-          "requires": {
-            "has-flag": "^4.0.0"
-          }
-        }
-      }
-    },
     "@types/json-schema": {
       "version": "7.0.4",
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz",
@@ -2493,7 +2481,6 @@
       "version": "5.1.4",
       "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.4.tgz",
       "integrity": "sha512-PZtnBuyfL07sqCJvGg3z+0+kt6fobc/xmle08jBiezLS8FrmGeiGkJnuxL/8Zgy9L83ypUhniV5atZn/L8n9MQ==",
-      "dev": true,
       "requires": {
         "@types/history": "*",
         "@types/react": "*"
@@ -2503,7 +2490,6 @@
       "version": "5.1.3",
       "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.3.tgz",
       "integrity": "sha512-pCq7AkOvjE65jkGS5fQwQhvUp4+4PVD9g39gXLZViP2UqFiFzsEpB3PKf0O6mdbKsewSK8N14/eegisa/0CwnA==",
-      "dev": true,
       "requires": {
         "@types/history": "*",
         "@types/react": "*",
@@ -3360,6 +3346,12 @@
       "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
       "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
     },
+    "auto-bind": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz",
+      "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==",
+      "dev": true
+    },
     "autoprefixer": {
       "version": "9.7.4",
       "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.4.tgz",
@@ -3515,6 +3507,16 @@
         }
       }
     },
+    "babel-literal-to-ast": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/babel-literal-to-ast/-/babel-literal-to-ast-2.1.0.tgz",
+      "integrity": "sha512-CxfpQ0ysQ0bZOhlaPgcWjl79Em16Rhqc6++UAFn0A3duiXmuyhhj8yyl9PYbj0I0CyjrHovdDbp2QEKT7uIMxw==",
+      "requires": {
+        "@babel/parser": "^7.1.6",
+        "@babel/traverse": "^7.1.6",
+        "@babel/types": "^7.1.6"
+      }
+    },
     "babel-loader": {
       "version": "8.0.6",
       "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz",
@@ -3593,6 +3595,12 @@
       "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
       "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U="
     },
+    "babel-plugin-syntax-trailing-function-commas": {
+      "version": "7.0.0-beta.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz",
+      "integrity": "sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==",
+      "dev": true
+    },
     "babel-plugin-transform-object-rest-spread": {
       "version": "6.26.0",
       "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz",
@@ -3607,6 +3615,41 @@
       "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz",
       "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA=="
     },
+    "babel-preset-fbjs": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.3.0.tgz",
+      "integrity": "sha512-7QTLTCd2gwB2qGoi5epSULMHugSVgpcVt5YAeiFO9ABLrutDQzKfGwzxgZHLpugq8qMdg/DhRZDZ5CLKxBkEbw==",
+      "dev": true,
+      "requires": {
+        "@babel/plugin-proposal-class-properties": "^7.0.0",
+        "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
+        "@babel/plugin-syntax-class-properties": "^7.0.0",
+        "@babel/plugin-syntax-flow": "^7.0.0",
+        "@babel/plugin-syntax-jsx": "^7.0.0",
+        "@babel/plugin-syntax-object-rest-spread": "^7.0.0",
+        "@babel/plugin-transform-arrow-functions": "^7.0.0",
+        "@babel/plugin-transform-block-scoped-functions": "^7.0.0",
+        "@babel/plugin-transform-block-scoping": "^7.0.0",
+        "@babel/plugin-transform-classes": "^7.0.0",
+        "@babel/plugin-transform-computed-properties": "^7.0.0",
+        "@babel/plugin-transform-destructuring": "^7.0.0",
+        "@babel/plugin-transform-flow-strip-types": "^7.0.0",
+        "@babel/plugin-transform-for-of": "^7.0.0",
+        "@babel/plugin-transform-function-name": "^7.0.0",
+        "@babel/plugin-transform-literals": "^7.0.0",
+        "@babel/plugin-transform-member-expression-literals": "^7.0.0",
+        "@babel/plugin-transform-modules-commonjs": "^7.0.0",
+        "@babel/plugin-transform-object-super": "^7.0.0",
+        "@babel/plugin-transform-parameters": "^7.0.0",
+        "@babel/plugin-transform-property-literals": "^7.0.0",
+        "@babel/plugin-transform-react-display-name": "^7.0.0",
+        "@babel/plugin-transform-react-jsx": "^7.0.0",
+        "@babel/plugin-transform-shorthand-properties": "^7.0.0",
+        "@babel/plugin-transform-spread": "^7.0.0",
+        "@babel/plugin-transform-template-literals": "^7.0.0",
+        "babel-plugin-syntax-trailing-function-commas": "^7.0.0-beta.0"
+      }
+    },
     "babel-preset-jest": {
       "version": "24.9.0",
       "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz",
@@ -5399,6 +5442,12 @@
       "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
       "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
     },
+    "dependency-graph": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.8.1.tgz",
+      "integrity": "sha512-g213uqF8fyk40W8SBjm079n3CZB4qSpCrA2ye1fLGzH/4HEgB6tzuW2CbLE7leb4t45/6h44Ud59Su1/ROTfqw==",
+      "dev": true
+    },
     "deprecated-decorator": {
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz",
@@ -6818,6 +6867,45 @@
         "bser": "2.1.1"
       }
     },
+    "fbjs": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-1.0.0.tgz",
+      "integrity": "sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA==",
+      "dev": true,
+      "requires": {
+        "core-js": "^2.4.1",
+        "fbjs-css-vars": "^1.0.0",
+        "isomorphic-fetch": "^2.1.1",
+        "loose-envify": "^1.0.0",
+        "object-assign": "^4.1.0",
+        "promise": "^7.1.1",
+        "setimmediate": "^1.0.5",
+        "ua-parser-js": "^0.7.18"
+      },
+      "dependencies": {
+        "core-js": {
+          "version": "2.6.11",
+          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
+          "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==",
+          "dev": true
+        },
+        "promise": {
+          "version": "7.3.1",
+          "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+          "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+          "dev": true,
+          "requires": {
+            "asap": "~2.0.3"
+          }
+        }
+      }
+    },
+    "fbjs-css-vars": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz",
+      "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==",
+      "dev": true
+    },
     "figgy-pudding": {
       "version": "3.5.1",
       "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz",
@@ -7638,6 +7726,17 @@
         "uuid": "^3.1.0"
       }
     },
+    "graphql.macro": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/graphql.macro/-/graphql.macro-1.4.2.tgz",
+      "integrity": "sha512-vcIaStPgS65gp5i1M3DSBimNVkyus0Z7k4VObWAyZS319tKlpX/TEIJSWTgOZU5k8dn4RRzGoS/elQhX2E6yBw==",
+      "requires": {
+        "@babel/template": "^7.4.4",
+        "babel-literal-to-ast": "^2.1.0",
+        "babel-plugin-macros": "^2.5.0",
+        "graphql-tag": "^2.10.1"
+      }
+    },
     "growly": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
@@ -8346,6 +8445,12 @@
       "resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz",
       "integrity": "sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg=="
     },
+    "immutable": {
+      "version": "3.7.6",
+      "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz",
+      "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks=",
+      "dev": true
+    },
     "import-cwd": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
@@ -12094,6 +12199,12 @@
         "boolbase": "~1.0.0"
       }
     },
+    "nullthrows": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz",
+      "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==",
+      "dev": true
+    },
     "num2fraction": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
@@ -14656,6 +14767,255 @@
       "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
       "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk="
     },
+    "relay-compiler": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/relay-compiler/-/relay-compiler-8.0.0.tgz",
+      "integrity": "sha512-JrS3Bv6+6S0KloHmXUyTcrdFRpI3NxWdiVQC146vD5jgay9EM464lyf9bEUsCol3na4JUrad4aQ/r+4wWxG1kw==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.0.0",
+        "@babel/generator": "^7.5.0",
+        "@babel/parser": "^7.0.0",
+        "@babel/runtime": "^7.0.0",
+        "@babel/traverse": "^7.0.0",
+        "@babel/types": "^7.0.0",
+        "babel-preset-fbjs": "^3.3.0",
+        "chalk": "^2.4.1",
+        "fast-glob": "^2.2.2",
+        "fb-watchman": "^2.0.0",
+        "fbjs": "^1.0.0",
+        "immutable": "~3.7.6",
+        "nullthrows": "^1.1.1",
+        "relay-runtime": "8.0.0",
+        "signedsource": "^1.0.0",
+        "yargs": "^14.2.0"
+      },
+      "dependencies": {
+        "@nodelib/fs.stat": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
+          "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
+          "dev": true
+        },
+        "braces": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "dev": true,
+          "requires": {
+            "arr-flatten": "^1.1.0",
+            "array-unique": "^0.3.2",
+            "extend-shallow": "^2.0.1",
+            "fill-range": "^4.0.0",
+            "isobject": "^3.0.1",
+            "repeat-element": "^1.1.2",
+            "snapdragon": "^0.8.1",
+            "snapdragon-node": "^2.0.1",
+            "split-string": "^3.0.2",
+            "to-regex": "^3.0.1"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "emoji-regex": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+          "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+          "dev": true
+        },
+        "fast-glob": {
+          "version": "2.2.7",
+          "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz",
+          "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==",
+          "dev": true,
+          "requires": {
+            "@mrmlnc/readdir-enhanced": "^2.2.1",
+            "@nodelib/fs.stat": "^1.1.2",
+            "glob-parent": "^3.1.0",
+            "is-glob": "^4.0.0",
+            "merge2": "^1.2.3",
+            "micromatch": "^3.1.10"
+          }
+        },
+        "fill-range": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+          "dev": true,
+          "requires": {
+            "extend-shallow": "^2.0.1",
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1",
+            "to-regex-range": "^2.1.0"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "glob-parent": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+          "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+          "dev": true,
+          "requires": {
+            "is-glob": "^3.1.0",
+            "path-dirname": "^1.0.0"
+          },
+          "dependencies": {
+            "is-glob": {
+              "version": "3.1.0",
+              "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+              "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+              "dev": true,
+              "requires": {
+                "is-extglob": "^2.1.0"
+              }
+            }
+          }
+        },
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "dev": true
+        },
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "kind-of": {
+          "version": "6.0.3",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+          "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+          "dev": true
+        },
+        "micromatch": {
+          "version": "3.1.10",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+          "dev": true,
+          "requires": {
+            "arr-diff": "^4.0.0",
+            "array-unique": "^0.3.2",
+            "braces": "^2.3.1",
+            "define-property": "^2.0.2",
+            "extend-shallow": "^3.0.2",
+            "extglob": "^2.0.4",
+            "fragment-cache": "^0.2.1",
+            "kind-of": "^6.0.2",
+            "nanomatch": "^1.2.9",
+            "object.pick": "^1.3.0",
+            "regex-not": "^1.0.0",
+            "snapdragon": "^0.8.1",
+            "to-regex": "^3.0.2"
+          }
+        },
+        "string-width": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^7.0.1",
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^5.1.0"
+          }
+        },
+        "to-regex-range": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+          "dev": true,
+          "requires": {
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1"
+          }
+        },
+        "yargs": {
+          "version": "14.2.2",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.2.tgz",
+          "integrity": "sha512-/4ld+4VV5RnrynMhPZJ/ZpOCGSCeghMykZ3BhdFBDa9Wy/RH6uEGNWDJog+aUlq+9OM1CFTgtYRW5Is1Po9NOA==",
+          "dev": true,
+          "requires": {
+            "cliui": "^5.0.0",
+            "decamelize": "^1.2.0",
+            "find-up": "^3.0.0",
+            "get-caller-file": "^2.0.1",
+            "require-directory": "^2.1.1",
+            "require-main-filename": "^2.0.0",
+            "set-blocking": "^2.0.0",
+            "string-width": "^3.0.0",
+            "which-module": "^2.0.0",
+            "y18n": "^4.0.0",
+            "yargs-parser": "^15.0.0"
+          }
+        },
+        "yargs-parser": {
+          "version": "15.0.0",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.0.tgz",
+          "integrity": "sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ==",
+          "dev": true,
+          "requires": {
+            "camelcase": "^5.0.0",
+            "decamelize": "^1.2.0"
+          }
+        }
+      }
+    },
+    "relay-runtime": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/relay-runtime/-/relay-runtime-8.0.0.tgz",
+      "integrity": "sha512-lOaZ7K/weTuCIt3pWHkxUG8s7iohI4IyYj65YV4sB9iX6W0uMvt626BFJ4GvNXFmd+OrgNnXcvx1mqRFqJaV8A==",
+      "dev": true,
+      "requires": {
+        "@babel/runtime": "^7.0.0",
+        "fbjs": "^1.0.0"
+      }
+    },
     "remark-html": {
       "version": "10.0.0",
       "resolved": "https://registry.npmjs.org/remark-html/-/remark-html-10.0.0.tgz",
@@ -15536,6 +15896,12 @@
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
       "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
     },
+    "signedsource": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/signedsource/-/signedsource-1.0.0.tgz",
+      "integrity": "sha1-HdrOSYF5j5O9gzlzgD2A1S6TrWo=",
+      "dev": true
+    },
     "simple-git": {
       "version": "1.131.0",
       "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.131.0.tgz",
@@ -16739,6 +17105,12 @@
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz",
       "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw=="
     },
+    "ua-parser-js": {
+      "version": "0.7.21",
+      "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz",
+      "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==",
+      "dev": true
+    },
     "unherit": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.2.tgz",

webui/package.json 🔗

@@ -8,12 +8,13 @@
     "@material-ui/icons": "^4.2.1",
     "@material-ui/lab": "^4.0.0-alpha.40",
     "@material-ui/styles": "^4.9.0",
-    "@types/jest": "^25.1.1",
     "@types/node": "^13.5.3",
     "@types/react": "^16.9.19",
     "@types/react-dom": "^16.9.5",
+    "@types/react-router-dom": "^5.1.3",
     "apollo-boost": "^0.4.7",
     "graphql": "^14.6.0",
+    "graphql.macro": "^1.4.2",
     "moment": "^2.24.0",
     "react": "^16.8.6",
     "react-apollo": "^3.1.3",
@@ -30,7 +31,9 @@
   "devDependencies": {
     "@graphql-codegen/cli": "^1.12.1",
     "@graphql-codegen/fragment-matcher": "^1.12.1",
-    "@types/react-router-dom": "^5.1.3",
+    "@graphql-codegen/near-operation-file-preset": "^1.12.1",
+    "@graphql-codegen/typescript-operations": "^1.12.1",
+    "@graphql-codegen/typescript-react-apollo": "^1.12.1",
     "eslint-config-prettier": "^6.10.0",
     "eslint-plugin-prettier": "^3.1.2",
     "prettier": "^1.19.1"

webui/src/Author.js 🔗

@@ -1,4 +1,3 @@
-import gql from 'graphql-tag';
 import Tooltip from '@material-ui/core/Tooltip/Tooltip';
 import MAvatar from '@material-ui/core/Avatar';
 import React from 'react';
@@ -15,17 +14,6 @@ const Author = ({ author, ...props }) => {
   );
 };
 
-Author.fragment = gql`
-  fragment authored on Authored {
-    author {
-      name
-      email
-      displayName
-      avatarUrl
-    }
-  }
-`;
-
 export const Avatar = ({ author, ...props }) => {
   if (author.avatarUrl) {
     return <MAvatar src={author.avatarUrl} {...props} />;

webui/src/Label.js 🔗

@@ -1,5 +1,4 @@
 import React from 'react';
-import gql from 'graphql-tag';
 import { makeStyles } from '@material-ui/styles';
 import {
   getContrastRatio,
@@ -48,15 +47,4 @@ function Label({ label }) {
   );
 }
 
-Label.fragment = gql`
-  fragment Label on Label {
-    name
-    color {
-      R
-      G
-      B
-    }
-  }
-`;
-
 export default Label;

webui/src/bug/Bug.graphql 🔗

@@ -0,0 +1,14 @@
+#import "../Label.graphql"
+#import "../Author.graphql"
+
+fragment Bug on Bug {
+  id
+  humanId
+  status
+  title
+  labels {
+    ...Label
+  }
+  createdAt
+  ...authored
+}

webui/src/bug/Bug.js → webui/src/bug/Bug.tsx 🔗

@@ -1,11 +1,11 @@
-import { makeStyles } from '@material-ui/styles';
+import { makeStyles } from '@material-ui/core/styles';
 import Typography from '@material-ui/core/Typography/Typography';
-import gql from 'graphql-tag';
 import React from 'react';
 import Author from '../Author';
 import Date from '../Date';
 import TimelineQuery from './TimelineQuery';
 import Label from '../Label';
+import { BugFragment } from './Bug.generated';
 
 const useStyles = makeStyles(theme => ({
   main: {
@@ -51,7 +51,11 @@ const useStyles = makeStyles(theme => ({
   },
 }));
 
-function Bug({ bug }) {
+type Props = {
+  bug: BugFragment
+};
+
+function Bug({ bug }: Props) {
   const classes = useStyles();
   return (
     <main className={classes.main}>
@@ -85,20 +89,4 @@ function Bug({ bug }) {
   );
 }
 
-Bug.fragment = gql`
-  fragment Bug on Bug {
-    id
-    humanId
-    status
-    title
-    labels {
-      ...Label
-    }
-    createdAt
-    ...authored
-  }
-  ${Label.fragment}
-  ${Author.fragment}
-`;
-
 export default Bug;

webui/src/bug/BugQuery.graphql 🔗

@@ -0,0 +1,9 @@
+#import "./Bug.graphql"
+
+query GetBug($id: String!) {
+  defaultRepository {
+    bug(prefix: $id) {
+      ...Bug
+    }
+  }
+}

webui/src/bug/BugQuery.js 🔗

@@ -1,30 +0,0 @@
-import CircularProgress from '@material-ui/core/CircularProgress';
-import gql from 'graphql-tag';
-import React from 'react';
-import { Query } from 'react-apollo';
-
-import Bug from './Bug';
-
-const QUERY = gql`
-  query GetBug($id: String!) {
-    defaultRepository {
-      bug(prefix: $id) {
-        ...Bug
-      }
-    }
-  }
-
-  ${Bug.fragment}
-`;
-
-const BugQuery = ({ match }) => (
-  <Query query={QUERY} variables={{ id: match.params.id }}>
-    {({ loading, error, data }) => {
-      if (loading) return <CircularProgress />;
-      if (error) return <p>Error: {error}</p>;
-      return <Bug bug={data.defaultRepository.bug} />;
-    }}
-  </Query>
-);
-
-export default BugQuery;

webui/src/bug/BugQuery.tsx 🔗

@@ -0,0 +1,20 @@
+import CircularProgress from '@material-ui/core/CircularProgress';
+import React from 'react';
+import { RouteComponentProps } from 'react-router-dom';
+
+import { useGetBugQuery } from './BugQuery.generated';
+import Bug from './Bug';
+
+type Props = RouteComponentProps<{
+  id: string
+}>;
+
+const BugQuery: React.FC<Props> = ({ match }: Props) => {
+  const { loading, error, data } = useGetBugQuery({ variables: { id: match.params.id } });
+  if (loading) return <CircularProgress />;
+  if (error) return <p>Error: {error}</p>;
+  if (!data?.defaultRepository?.bug) return <p>404.</p>;
+  return <Bug bug={data.defaultRepository.bug} />;
+};
+
+export default BugQuery;