├── .vscode └── settings.json ├── docs ├── highlighted-enums-1.png ├── highlighted-types-1.png ├── highlighted-types-2.png ├── highlighted-inputs-1.png └── highlighted-interfaces-1.png ├── package.json ├── tsconfig.json ├── .gitignore ├── readme.md └── index.ts /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "Normalizr", 4 | "Splitted", 5 | "TODOs" 6 | ] 7 | } -------------------------------------------------------------------------------- /docs/highlighted-enums-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Svehla/Typescript-GraphQL-AST-parser/HEAD/docs/highlighted-enums-1.png -------------------------------------------------------------------------------- /docs/highlighted-types-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Svehla/Typescript-GraphQL-AST-parser/HEAD/docs/highlighted-types-1.png -------------------------------------------------------------------------------- /docs/highlighted-types-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Svehla/Typescript-GraphQL-AST-parser/HEAD/docs/highlighted-types-2.png -------------------------------------------------------------------------------- /docs/highlighted-inputs-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Svehla/Typescript-GraphQL-AST-parser/HEAD/docs/highlighted-inputs-1.png -------------------------------------------------------------------------------- /docs/highlighted-interfaces-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Svehla/Typescript-GraphQL-AST-parser/HEAD/docs/highlighted-interfaces-1.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-graphql-ast-parser", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Svehla/Typescript-GraphQL-AST-parser.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/Svehla/Typescript-GraphQL-AST-parser/issues" 17 | }, 18 | "homepage": "https://github.com/Svehla/Typescript-GraphQL-AST-parser#readme", 19 | "dependencies": { 20 | "typescript": "^4.2.0-beta" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["esnext", "es2018.promise", "es2017", "esnext.asynciterable"], 5 | "allowJs": true, 6 | "downlevelIteration": true, 7 | "skipLibCheck": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "commonjs", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": false, 15 | "experimentalDecorators": true, 16 | "noEmit": true, 17 | "jsx": "preserve", 18 | "esModuleInterop": true, 19 | "noImplicitReturns": true, 20 | "noImplicitThis": true, 21 | "strictNullChecks": true, 22 | "noImplicitAny": true, 23 | "noUnusedParameters": false, 24 | "noUnusedLocals": false 25 | }, 26 | "include": [ 27 | "./index.ts", 28 | ] 29 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Node ### 2 | 3 | # Logs 4 | logs 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | .bypassDeepRecurisvePOCTest.ts 10 | 11 | # temporary files for generating a encrypting pdfs 12 | temp/* 13 | !temp/readme.md 14 | 15 | # Test coverage 16 | .nyc_output/**/* 17 | 18 | # Optional npm cache directory 19 | .npm 20 | 21 | # Dependency directories 22 | /node_modules 23 | /jspm_packages 24 | /bower_components 25 | 26 | # Yarn Integrity file 27 | .yarn-integrity 28 | 29 | # Optional eslint cache 30 | .eslintcache 31 | 32 | # dotenv environment variables file(s) 33 | .env 34 | 35 | #Build generated 36 | dist/ 37 | build/ 38 | 39 | # Serverless generated files 40 | .serverless/ 41 | 42 | ### SublimeText ### 43 | # cache files for sublime text 44 | *.tmlanguage.cache 45 | *.tmPreferences.cache 46 | *.stTheme.cache 47 | 48 | # workspace files are user-specific 49 | *.sublime-workspace 50 | 51 | # project files should be checked into the repository, unless a significant 52 | # proportion of contributors will probably not be using SublimeText 53 | # *.sublime-project 54 | 55 | ### Vim ### 56 | *.sw[a-p] 57 | 58 | ### WebStorm/IntelliJ ### 59 | /.idea 60 | modules.xml 61 | *.ipr 62 | 63 | 64 | ### System Files ### 65 | *.DS_Store 66 | 67 | # Windows thumbnail cache files 68 | Thumbs.db 69 | ehthumbs.db 70 | ehthumbs_vista.db 71 | 72 | # Folder config file 73 | Desktop.ini 74 | 75 | # Recycle Bin used on file shares 76 | $RECYCLE.BIN/ 77 | 78 | # Thumbnails 79 | ._* 80 | 81 | src/__generated__ 82 | # Files that might appear in the root of a volume 83 | .DocumentRevisions-V100 84 | .fseventsd 85 | .Spotlight-V100 86 | .TemporaryItems 87 | .Trashes 88 | .VolumeIcon.icns 89 | .com.apple.timemachine.donotpresent -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Typescript GraphQL AST Parser 2 | 3 | !!! DON'T USE THIS CODE IN THE PRODUCTION !!! 4 | 5 | 6 | If you want to interactive play with this source code, just open index.ts and set typescript version to the newest one. 7 | 8 | 9 | This repository is just my fun project where I demonstrate power of typescript by writing Graphql parser in pure Type system. 10 | 11 | ## Preview 12 | 13 | you can interactive play with the TS-GQL parser [there](https://www.typescriptlang.org/play?target=1#code/PQKgBApgzgNglgOwC4FoAmcoEMBGMJgC2WAHivgmCMALABQoksiqG2eBAAkgJ4AO0AMYAnOH1TR4yYAgD2KAK4IFUCGhQA3LMKhVadesGBgUps+YuWrZw8evmwCpHBi77Nhnfff79CCT5ZYSQwXgEwAGEsKCQAHgAVABowAFUAPjAAXjB4yBIkCAQ0XRSwAH4csAAuVPowggAJCCw0BIzs3P8Cot0AbUQAMwhhMAAxOB0kAEkCwmSAOkXB4bAAfQAlaCQAXXKxiZiZiEJqsAQIDWG6-gJ4rBc2rMquwuKwfoQhkdXxyaO5sCLebLEabGK7CpgkI1c6XYT0a7hTaEWSXADKSG0SAA6gALOAFNF8LCCaCPDp5bpvAAGYAAJABvEE5AC+tMhx1REAxWLxBO5xNJUEeNXiiIIyK5AFEinzCYKyfF2s98q9dNTGcz4iywOywJLLjK0HKBSTFRlRQjPDkAPIAERtNWIAGsCABzQrDOCCUKyMBwN1yYQEAA6lCwIRiWN0WCKoVxBFeYGDggUOjglxgPHF+s56MxwSNABkEOSVVT1WHNZ8Vtq9QbuQWkMXS0rTmLrfVcyjDUUW2XOqqerrq19WWH63mIEb+23LQY6F34qJCLF6GAwEZ1zlEtv4gAGJ4NnnBE1Es3CpW7ugb+IARiPU6NZ4Vl-3aXoyvvhhAOfiWwiWQEDhJAAEFhGEKZkFkG0cAAKyeQDgOGMCIKgpAYPgiBBDibdem3DcGX9NAagAcjgNA71I5ItBgMi7xIRiSFIllrw3QjiLIiiACZqLAWiyO4pjmNYgiwCIiiuLQABmPiBLAUjpOEli2LAbZVPItBSM-ehqBzJCQPAyDoNguDsNwm9yzVcT3gAaX9ShbIgHhthqGJRAQN0wBZXp1O3JyeEpaz3MQN1VKMgA1LAYAUaAnniXplEIHBhj8yyAt0DpEoUZLUt6AK0o3UyngZMT8ocyJojiDLkhCzy0lcsApXyYQSTiSLotiqBkiI3pXUCxAwAKmp7JZD9LJZT8nlMq0jBMawwCMrAeBQItEDipwXDcHwPBzABZLA+FWZc4FXNsKReYdfL2a6al6E7VyaFo2jSBZFgOo6HoSe4YBe7ZZuMJcV1AmAYDW85LyC4c6rddorS7cYYAKYQpUIcQeH+SHLreGHfK-KG3l87cKiJyyaie1o22x3RSO0yyKjm7goBQfwBBwlmIKCMTEeR1H0cx76HiVcaNxqRmkGZ1nzI54Qucsjdegpto3vmHnhj53gBbuIW0ga+GbjAIl4CQAAhHgEgJ3QYeSO0IHgQh+RGamwBh-Hnepalifef6yas4cNSZGsRlAllGVt+3HdDwOxxNtkvd6UCVaNgkzdiE2bbt07Hb1337p9nMAClZEQC3ndjHhfIziPkctl2kA82H4tr0mNwqWntzF4wmZZgJpeGWWRjKIeyjEgOlaVKPw6z5Go6Lkvtd+q8wCnh3kbSOOFy7ZPTZ4QDCGILGhxx+vQuVbfU6SBTElI8at74Y2zfBxVa9dp5z-Ny-SLDG+c3f0C0eGNgIopcj5WxPvVN+98U4f2SKRAAZD-TeBs54ICMmbAAchAAA7k-EBFY64Nzxk8FBCRYHf3GjmTY98zR7wPvEWQmCcHrWFHuF+4Cwp7kPM7XGuxsjv1odEZWe4HzZBQWgngjDcEHnGl+O8+twj8NkPvLAoEihPxYZZQc+DaaqQPGwhuj5qGkgEVAehkjmFCM0SIw2UCd7qISO+KaHQ5ELjmjtdx5hbDzSsGAQQSjCCFAlt43w1oPFhPkRKKcABxAAikWWhgSNG3n0aFVS6jIEPx4PYq8251HYgJLiWQTh0GyAKJlfYSNhirFiUWJ+UwoAJOQLEdRr1tzi0lr3dm-c5YbgiPgbQagYmxWEIFURxdUEQQwdg3BeSClFKQCUspMjEL9ODGgIZwxsxIPCPUp+J4Jb5KQLiBo0RcR3DdAOWu1IADEo4VgAA02R7HrrFU4AxoqqABraB0NQWhoBdgoPggRghgAGEEMANSwBoCEKIcQcAgJQBzDUupDT-GBLLLs9a+yoCHOOac85bQIkVORtUuJKLGlxHOn7QmPtW7ew7hCsl616kUtiOPXWtcXkQC9mrYQpLanMtRfvdFC8XoMsVs0Smr1ASLF5fy8laKmmiuFvnUJC1IWgTRLkAACtoVQOhgmeLVWE8JxqLBgH6jUWirziQ6BWFYLxJqTVfPiPiXQHpziiB9GgWQcU5AhFtaoC1zl+LRTAFggpYBtBuhyoknMuq7VolOvfCAAUooxQgAOFcjx3Z3JGAFFkVQ81gHTbFDedLSry3YpanI2aAoi3Yhua1EBRTZtLRABtG5Jq+2UKDIlH1VgJtUEmtG+A02dUzVSrR1kW43VpacXoQ7uTJrHc5dtbLJUvRVgOpdI6U3jozYLReutVVdiXesLAWCangTdO23Ftt3kKCRjaOFQFLm5ujisO0EYsDxBuCyTIxaH1YCfUgdt5a9iVsbU2idNRz2Xpqeu79mI-0CE7exaFj6kbttbadWIwHQPtvQ920W4kxIwYzXBvVEAL1XrieupU5GoUQCw2B2D-Fi5oDEt2+N1HaOIYnWWKJEA2MZtQ5O7NwsiXCdE7FcT77QEjk-SMcTLIACEnt6YQpE+2paPB5PiYbTUGTumILLQMxkAAPmcJ9MBpM6YnXpizTd3a9GLUZFk2xNN0rmimNMUB4WUAwmAWQr6EBhu0CMLssgBghbC2G5tFUjkEEi8tMS8G6NFnXUZNIs7jMOYzSbaIEALP2dkxAIrqhnMXUU6RDEDc6Z0phgy6dw5SLoUa3sJKKV4S+1a28UiowYCyAjJ1io3Wrh9drqRE2shZD9IQGNsAOA5sLZa18p1O1HXmshVBPgThQgGxCW4zb3giXNXrm1Gpe2nDiagKBGIa5NEpM8qpWj4n6uhXKUuqJABHGAN3KWtMsnpm0AxAflJ3Xxy9gOPvsNiO9m4n3PJQGBxudpPc2YSE5r1jc6w5vTAQPtiWplzKISAoZNCJksI4ViKD8HROnDdQUvUdBWAAk31UoD0nOEoAFxUKbWQaBRlkarRjqWXScdlXsoNfqMX9QE4hzznYNR8elKVzTpA+Vti9FIit4XpF53duVNzzXfOBezeF0Sn7-3AdPeSdw9hTjqXqmLasPEEYLjDBZGGRAxP6TKbAIDtnASWQMmLYDy3PAWRR2ZFCCDJMiLhYCTUYP7OIAAG5ltC54KnxngvhfeRVjbgH+eEdbBzqR0m+1DqDuh1g2HSP4dTubvOkm87O5gG7hL7HA8yoZcbwIZHFz2XSqBFDu1tHB+Ngbkev61v6-T+H-bl3Nlk8toIaFLP+vc+b88kXtpXcJaY77lLyyUftWyz4FAA9TwJ-DpXamtdgnFHKNUWgbJuud+G91qpcXnTe8ekg988oBldydkJggjJ0JMIzJacL8r8b9n8YBYF+pOdtwo8SoxJ-8scZYgCFYZdKA5dYsNdYCVcwAMsBND0SDzJtdddm0f9txjdMDLJWd09cMzpdd18GDLId8ago96AeMzVTsHUhCUBGUiwHJkZ3khRDUjUTthDrBzsWors4l0JhhpDoBNV4gV9+swEG43sYdkB1CzRh9vtqM-tS8pCzRLF0cvAxDgwVACA-cDsoxghtpTBD9w0CAHCg1hs3RvRQVZYThIVnCQguwsF8RBBcQwBcRogXZ09s9C93IFAcI0wUtdA1DhANDEitkNx6dMiNDIda8B9884dZ8p8jCsiTD2FUc-8j8OkcDulccFd1dKiNCwDsgDIUIoDqdSC6czMeAwcCiLxYFWCOc0dNw7DkwIBHCHJ-drIjUNxhjSRldzcYgMDsgoN2JsDT8+8q1ypZdnJ5c1dCcrCVjNdGoTjliIBldaC9cc9DdGDndrjVj+d1ic8F87ULDridCXs3RnddClNmR3dYiCg4QfcEAWBjDSQA9mRriQ8IAw8I82izQo8Y9i149vMbok82DgCziIAETt8c8898SMDWIZV5gS8fioRK8F1VV5CFDLBtsfFIUuwfBmTGSLAlDLscIak7stDfjHd9DtxEch8aingS87sbCcgbh7sII79ij68yjQoEdL1lSUcJidjJcB5EVNFZTipOiKduiqcMJlcEhZSjJRibgES0DLI9phgPQ7tmDG0tTADmj8CKoiCZSBBQD4JGo7tTI7jv8jdnd7ThBHTZSXV7RHQo00B-lH8AlkBdB+osEgh-koBAVgUkBeMvj-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-Pj398QX5Wk3-sSv+zpT2u9UmPIXWPoD2OcOXPUOeP5K+PNPBO7IfWeB5cuudBldGpy4niM2nXNON2LDZudDDPFNs2xxc3yyzOwwoBsvtAnORgAuuO8H3OOzP6tMly1zfPBKvpzuIOeG0b8n2Jov1vjvhAe2xIkv-3Uu5D8ufFiBBpTV0uYOkURMLCBSMPoZqu9xD61B0lCz98xlEBxFzFzhYhhrs135sldZl7Vk1BAIAHUevIN7ok4kKVLwkeP9mEJialZuxX16mplDeTivvu0O4h6nSeO1VIak4utCnh4OOfEOhvNDHtef7bGfVDSiLStURf2ekBrt5efTyvYhpfoVZeix+TFfshReVeTn1fxWtf+ftxhX-XheDflfLfRWBSzedfriWeleeSjeixneHfie0A+flkUWNwjvSudBjNOeg+WftxnvUOQ-xfLPUOtCcbZTo+9eOwNwC3lXo-TX4-LJQiHrVfidk-tw4rCjo-PetU-2Fw-EEAYhDsBAH1yksTA-+zG1kMIAXENwi+zRUr7bxJJIg87Q1N0brk5iDsKU1BdV-DwtByEBtUbQIgbJQiSS1NvJC-88WKJ+-zKAiIXHh9l-LI0+wAbRhBoVhAzYApUyj-xJNUIhEg7QpQ0QIh0b9+0+y6iJE4wXkg5-tw7R0bI+uBMyVghyowKYFKEQ6rBb+QA9BFMHiBTAbQ6CBPuED2hOAN+jDDcJX0xA4RRgQQQgCrQX4H8j+wwU-s5HP5oA1MpDQ2Omy7TwCCAGyEZA1RTTnldAJSABnAi77QoX+YkTahvnQjJBnASAfAAHUPLVxF+TwO8My0PIxYBgqgJAHdHQhqZtgamHgfiCP43oagw+UgRoyQFT81MHApwIUmEA1AhsI2JANoImj0BNMOZVQGgCiStQ+AuITltkBkww9xW9QeXPUDr4UJ9mBAV1pR2oxWCbBdgosLrjT5QBHing0DrHzig+C7Ufgw6AEN1y-8QhPsLeFzxa5qBrBsQuJLrkb56pQhXYZ3qkJiG2DMh5EFEkKFyEGwnSUQywekKKGBCr2PpQ3LrhoE-skhBsCHAUJqFxCShxORIVaCAA) 14 | 15 | 16 | ![Parser preview](/docs/highlighted-types-1.png) 17 | 18 | ![Parser preview](/docs/highlighted-interfaces-1.png) 19 | 20 | ![Parser preview](/docs/highlighted-types-2.png) 21 | 22 | ![Parser preview](/docs/highlighted-enums-1.png) 23 | 24 | ![Parser preview](/docs/highlighted-inputs-1.png) 25 | 26 | 27 | 28 | ## TS Parser supports 29 | 30 | - Comments 31 | - Single line comments ✅ 32 | - Types ✅ 33 | - Enums ✅ 34 | - Input Types ✅ 35 | - Fields ✅ 36 | - Basic Values ✅ 37 | - String ✅ 38 | - Boolean ✅ 39 | - Float ✅ 40 | - Int ✅ 41 | - Nullable Fields ✅ 42 | - Array Fields ✅ 43 | - Arguments ✅ 44 | - Default Parameters ✅ 45 | - Fields ✅ - Basic Values ✅ 46 | - String ✅ 47 | - Boolean ✅ 48 | - Float ✅ 49 | - Int ✅ 50 | - Nullable Fields ✅ 51 | - Array Fields ✅ 52 | - Interfaces 53 | - definition ✅ 54 | - implements keyword ✅ 55 | - Mutations ✅ 56 | - Directives ✅ 57 | - option A | B | C values: ❌ 58 | - Scalars ✅ 59 | - Descriptions ❌ 60 | 61 | (Btw Source code is full of bugs and edge case issues...) 62 | 63 | 64 | 65 | ## Example of parser usage 66 | 67 | This is example of using GQL TS parser 68 | 69 | 70 | Let's have this graphQL Schema 71 | 72 | ```graphql 73 | scalar Date1 74 | interface Node {id: ID!} 75 | # input CommentedPaginationPOC { input: Int! } 76 | input Pagination { value: String } 77 | enum OrderByKeyword { 78 | ASC 79 | DESC 80 | } 81 | directive @upper on FIELD_DEFINITION! 82 | type Mutation { 83 | contactForm( 84 | input: OrderByKeyword!, 85 | ): String 86 | } 87 | type Query implements Node & Node2 { 88 | age: Int 89 | title(offset: Int!, thirdArg: String!): String! 90 | author: Float! 91 | } 92 | ``` 93 | 94 | just by calling 95 | 96 | ```typescript 97 | type ParsedGraphQL = GetGqlAST 98 | ``` 99 | 100 | `GetGqlAST` generic will infer type with this shape: 101 | 102 | 103 | ```typescript 104 | { 105 | interfaces = { 106 | Node: { 107 | id: "ID"; 108 | }; 109 | }; 110 | enums: { 111 | OrderByKeyword: "ASC" | "DESC"; 112 | }; 113 | inputs: { 114 | Pagination: { 115 | value: { 116 | value: string | null; 117 | }; 118 | }; 119 | }; 120 | types: { 121 | Mutation: { 122 | implements: []; 123 | fields: { 124 | contactForm: { 125 | args: { 126 | input: { 127 | value: "OrderByKeyword"; 128 | defaultValue: void; 129 | }; 130 | }; 131 | value: string | null; 132 | }; 133 | }; 134 | }; 135 | Query: { 136 | implements: ["Node", "Node2"]; 137 | fields: { 138 | title: { 139 | args: { 140 | limit: { 141 | value: number | null; 142 | defaultValue: "10"; 143 | }; 144 | offset: { 145 | value: number; 146 | defaultValue: null; 147 | }; 148 | thirdArg: { 149 | value: string; 150 | defaultValue: null; 151 | }; 152 | }; 153 | value: string; 154 | }; 155 | author: { 156 | args: {}; 157 | value: number; 158 | }; 159 | age: { 160 | args: {}; 161 | value: number | null; 162 | } 163 | } 164 | } 165 | }, 166 | scalars: { 167 | Date1: any; 168 | }, 169 | Directives: { 170 | upper: "FIELD_DEFINITION"; 171 | } 172 | } 173 | ``` 174 | 175 | ![Parser preview](/docs/highlighted-types-1.png) 176 | 177 | ## My TODOs: 178 | 179 | ### Max recursion stack calling 180 | Now there is no possible to have more then +- 21 lines, 21 types, 21 inputs, 21 enums etc... its because TS recursion deep level 181 | 182 | So I have to resolve how to bypass recursion deep level. It could be possible by extract splitting arrays into more smaller chunks called in more recursion stacks and then joined by some heuristic rule. 183 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | /* eslint-disable @typescript-eslint/no-unused-vars */ 3 | 4 | // ------------------------------- 5 | // ------------ utils ------------ 6 | // ------------------------------- 7 | export type Cast = T extends U ? T : U 8 | type Head = T extends [infer FirstItem, ...infer _Rest] ? FirstItem : never 9 | type Tail = T extends [infer _FirstItem, ...infer Rest] ? Rest : never 10 | 11 | type RemoveStartWhiteSpaces = T extends ` ${infer T}` ? RemoveStartWhiteSpaces : T 12 | type RemoveEndWhiteSpaces = T extends `${infer T} ` ? RemoveEndWhiteSpaces : T 13 | 14 | // TODO: make generic to ignore \n at starts and the end recursively 15 | type RemoveStartEndLn = T extends `\n${infer T}` ? RemoveStartEndLn : T 16 | // type RemoveEndEndLn = T extends `${infer T}\n` ? RemoveEndEndLn : T 17 | 18 | type Trim< 19 | // 20 | T, 21 | T0 = RemoveStartWhiteSpaces, 22 | T1 = RemoveEndWhiteSpaces 23 | > = T1 24 | /* 25 | type TestConvertArrIntoObj = NormalizeArray< 26 | [ 27 | { id: 'id1', val: '1xxxx'}, 28 | { id: 'id2', val: '2xxxx'}, 29 | { id: 'id3', val: '3xxxx'}, 30 | ], 31 | 'id' 32 | > 33 | */ 34 | // inspiration to name this generic `NormalizeArray` comes from normalizr library 35 | // which do the same data transformation 36 | // > (source)[https://github.com/paularmstrong/normalizr] 37 | type NormalizeArray< 38 | T extends { [K in Key]: string }[], 39 | Key extends string, 40 | ArrValues = T[number], 41 | Keys = T[number][Key], 42 | Obj = { 43 | [K in Cast]: Extract 44 | } 45 | > = Obj 46 | 47 | // --------- Array-Lines utils ---------------- 48 | 49 | type Map_Trim = T extends [] ? [] : [Trim>, ...Map_Trim>] 50 | 51 | // type TrimAllLines = 52 | 53 | type IsTextEmpty = T extends '' ? true : false 54 | 55 | type Filter_IsTextEmpty = T extends [] 56 | ? [] 57 | : IsTextEmpty> extends true 58 | ? Filter_IsTextEmpty> 59 | : [Head, ...Filter_IsTextEmpty>] 60 | 61 | type SplitBy = T extends `` 62 | ? [] 63 | : T extends `${infer A}${Delimiter}${infer B}` 64 | ? [A, ...SplitBy] 65 | : [T] 66 | 67 | type Join = T extends [] 68 | ? '' 69 | : // @ts-expect-error ???? 70 | `${Head}${Delimiter}${Join, Delimiter>}` 71 | 72 | type SplitByCommas = SplitBy 73 | type SplitByLines = SplitBy 74 | type SplitByAmpersand = SplitBy 75 | 76 | type JoinArrByNewLine = Join 77 | 78 | type ReplaceCommasToNewLines< 79 | T extends string, 80 | T0 extends string[] = SplitByCommas, 81 | T1 = JoinArrByNewLine 82 | > = T1 83 | 84 | type SplitByCommaAndLines< 85 | T extends '', 86 | T0 extends string = ReplaceCommasToNewLines, 87 | T1 = SplitByLines 88 | > = T1 89 | 90 | // ------------------------------- 91 | // --------- comments ------------ 92 | // ------------------------------- 93 | 94 | type RemoveGQLComments< 95 | T extends string, 96 | Lines = SplitByLines, 97 | LinesWithoutNotes = Filter_IsLineGQLComment, 98 | // @ts-expect-error 99 | ClearedQuery = JoinArrByNewLine 100 | > = ClearedQuery 101 | 102 | type IsLineStartsWithHashTag = T extends `#${infer _X}` ? true : false 103 | 104 | // TODO: add support for GQL descriptions 105 | type IsLineGQLComment = IsLineStartsWithHashTag 106 | 107 | type Filter_IsLineGQLComment = T extends [] 108 | ? [] 109 | : IsLineGQLComment> extends true 110 | ? Filter_IsLineGQLComment> 111 | : [Head, ...Filter_IsLineGQLComment>] 112 | 113 | // --------- GQL AST Parsers -------- 114 | // ---------------------------------- 115 | // -------- key: value parser ------- 116 | // ---------------------------------- 117 | 118 | // This generic does not parse key val with arguments 119 | type ParseSimpleKeyValue = Trim extends `${infer Key}:${infer Value}` 120 | ? { 121 | key: Trim 122 | value: Trim 123 | } 124 | : null 125 | 126 | type Map_ParseSimpleKeyValue = T extends [] 127 | ? [] 128 | : [ParseSimpleKeyValue>, ...Map_ParseSimpleKeyValue>] 129 | 130 | type ParseRawGQLArgValueWithDefaultOption = T extends `${infer DataType}=${infer DefaultValue}` 131 | ? { 132 | value: ParseRawGQLValue 133 | defaultValue: Trim 134 | } 135 | : { 136 | value: ParseRawGQLValue 137 | defaultValue: void 138 | } 139 | 140 | type ParseRawGQLValue = GetValueType> 141 | 142 | type GetValueType = T extends `${infer Type}!` 143 | ? GetValueArrayType 144 | : GetValueArrayType | null 145 | 146 | type GetValueArrayType = T extends `[${infer Arr}]` 147 | ? // recursion to optional arr type of optional value in the array 148 | ParseRawGQLValue[] 149 | : GetValueBaseType 150 | 151 | type GetValueBaseType = T extends 'String' 152 | ? string 153 | : T extends 'Int' 154 | ? number 155 | : T extends 'Float' 156 | ? number 157 | : T extends 'Boolean' 158 | ? boolean 159 | : T 160 | 161 | // ------------------------------- 162 | // ------ GQL Input type --------- 163 | // ------------------------------- 164 | 165 | type ExtractGQLInputTypesAst< 166 | T extends string, 167 | RawTypeStrings = ParseGqlInput, 168 | ArrayOfInputs = Map_ParseRawInputTypeString, 169 | // @ts-expect-error 170 | RootInputsObject = NormalizeArray, 171 | InputObjectsJustBody = { 172 | // @ts-expect-error 173 | [K in keyof RootInputsObject]: RootInputsObject[K]['body'] 174 | } 175 | > = InputObjectsJustBody 176 | 177 | type ParseGqlInput< 178 | T extends string 179 | > = T extends `${infer _Whatever}\ninput ${infer InputName}{${infer InputBody}}${infer Rest}` 180 | ? [{ name: InputName; body: InputBody }, ...ParseGqlInput] 181 | : [] 182 | 183 | type Map_ParseRawInputTypeString = T extends [] 184 | ? [] 185 | : // @ts-expect-error 186 | [ParseRawInputTypeString>, ...Map_ParseRawInputTypeString>] 187 | 188 | type ParseRawInputTypeString< 189 | T extends { name: string; body: string }, 190 | // @ts-expect-error 191 | BodyPropsKeyVal = Map_ParseSimpleKeyValue>, 192 | // @ts-expect-error 193 | InputsObject = NormalizeArray, 194 | Body = { 195 | // @ts-expect-error 196 | [K in keyof InputsObject]: ParseRawGQLValue 197 | } 198 | > = { 199 | typeName: Trim 200 | body: Body 201 | } 202 | 203 | // ------------------------------- 204 | // ------ GQL interfaces --------- 205 | // ------------------------------- 206 | 207 | type ExtractGQLInterfacesAST< 208 | T extends string, 209 | RawInterfaceStrings = ParseGqlInterface, 210 | // --- reuse input starts ---- 211 | // we reuse logic from GQL input type which has same body structure as Interface body 212 | ArrayOfInterfaces = Map_ParseRawInputTypeString, 213 | // @ts-expect-error 214 | RootInterfaceObject = NormalizeArray, 215 | // --- reuse input ends ---- 216 | InterfaceObjectsJustBody = { 217 | // @ts-expect-error 218 | [K in keyof RootInterfaceObject]: RootInterfaceObject[K]['body'] 219 | } 220 | > = InterfaceObjectsJustBody 221 | 222 | type ParseGqlInterface< 223 | T extends string 224 | > = T extends `${infer _Whatever}\ninterface ${infer InterfaceName}{${infer InterfaceBody}}${infer Rest}` 225 | ? [{ name: InterfaceName; body: InterfaceBody }, ...ParseGqlInterface] 226 | : [] 227 | 228 | // ------------------------------- 229 | // --------- GQL type ------------ 230 | // ------------------------------- 231 | 232 | type ExtractGQLTypesAST< 233 | T extends string, 234 | RawTypeStrings = ParseGqlTypes, 235 | TypesArr = Map_ParseRawTypeString, 236 | // @ts-expect-errors 237 | TypesObj = NormalizeArray, 238 | MergeTypes = { 239 | // @ts-expect-error 240 | [K in keyof TypesObj]: TypesObj[K]['body'] 241 | } 242 | > = MergeTypes 243 | 244 | // TODO: add implements keyword support 245 | type ParseGqlTypes< 246 | T extends string 247 | > = T extends `${infer _Whatever}\ntype ${infer TypeDeclaration}{${infer TypeBody}}${infer Rest}` 248 | ? TypeDeclaration extends `${infer TypeName} implements ${infer Implements}` 249 | ? [ 250 | { 251 | name: TypeName 252 | body: TypeBody 253 | implements: Implements 254 | }, 255 | ...ParseGqlTypes 256 | ] 257 | : [ 258 | { 259 | name: TypeDeclaration 260 | body: TypeBody 261 | implements: null 262 | }, 263 | ...ParseGqlTypes 264 | ] 265 | : [] 266 | 267 | type Map_ParseRawTypeString = T extends [] 268 | ? [] 269 | : // @ts-expect-error 270 | [ParseRawTypeString>, ...Map_ParseRawTypeString>] 271 | 272 | type ParseRawTypeString< 273 | T extends { implements: any; name: string; body: string }, 274 | TypeName = Trim, 275 | BodyPropsKeyValArr = ParseTypeKeyValuesWithArgs, 276 | // @ts-expect-error 277 | BodyPropsKeyValObj = NormalizeArray, 278 | Body = { 279 | // now the GQL parser supports only 1 interface per type. No idea if real GQL supports more 280 | // TODO: check that + implement if yes 281 | implements: T['implements'] extends null ? [] : Map_Trim> 282 | fields: { 283 | // ---- gaga magic ---- 284 | [K in keyof BodyPropsKeyValObj]: { 285 | // @ts-expect-error 286 | args: ParserRawGQLTypeBodyArgsPropString 287 | // @ts-expect-error 288 | value: ParseRawGQLValue 289 | } 290 | } 291 | } 292 | > = { 293 | typeName: TypeName 294 | body: Body 295 | } 296 | 297 | // Shitty tricky parser which try to resolve if key: value has some args 298 | // just by checking if the name includes `(` bracket character 299 | // BTW: there is duplicate code for simple non arguments key recursion :( 300 | // TODO: add better docs for this pice of shitty magic 301 | // TODO: what about to split to 2 function one for parsing infer value, second for spreading arrays? 302 | type ParseTypeKeyValuesWithArgs< 303 | T extends string, 304 | TrimmedT extends string = Trim, 305 | ClearedT = ReplaceCommasToNewLines 306 | > = ClearedT extends `` 307 | ? [] 308 | : ClearedT extends `${infer Key}:${infer Value}\n${infer PureRest}` 309 | ? // if include bracket => key has args... 310 | Key extends `${infer What1}(${infer Args}` 311 | ? ClearedT extends `${infer KeyName}(${infer Args}):${infer ValByArgs}\n${infer Rest}` 312 | ? [ 313 | { 314 | key: Trim> 315 | // enable split args by 316 | // 1) new line 317 | // 2) comma 318 | args: Map_ParseSimpleKeyValue< 319 | // @ts-expect-error 320 | Filter_IsTextEmpty> 321 | > 322 | value: Trim 323 | }, 324 | ...ParseTypeKeyValuesWithArgs 325 | ] 326 | : [ 327 | { 328 | key: Trim> 329 | args: [] 330 | value: Trim 331 | }, 332 | ...ParseTypeKeyValuesWithArgs 333 | ] 334 | : ClearedT extends `${infer Key}:${infer Value}\n${infer Rest2}` 335 | ? [ 336 | { 337 | key: Trim> 338 | args: [] 339 | value: Trim 340 | }, 341 | ...ParseTypeKeyValuesWithArgs 342 | ] 343 | : [] 344 | : [] 345 | 346 | type ParserRawGQLTypeBodyArgsPropString< 347 | // TODO: T should be only arg, not whole body 348 | T extends { key: string; value: string }[], 349 | BodyPropsObj = NormalizeArray, 350 | T0 = { 351 | [K in keyof BodyPropsObj]: ParseRawGQLArgValueWithDefaultOption< 352 | // @ts-expect-error 353 | BodyPropsObj[K]['value'] 354 | > 355 | } 356 | > = T0 357 | // ------------------------------- 358 | // --------- GQL enum ------------ 359 | // ------------------------------- 360 | type ExtractGQLEnumsAST< 361 | T extends string, 362 | RawTypeStrings = ParseGqlEnums, 363 | SplitsArr = Map_ParseRawEnumString, 364 | // @ts-expect-error 365 | EnumsObject = NormalizeArray, 366 | MergeEnums = { 367 | // @ts-expect-error 368 | [K in keyof EnumsObject]: EnumsObject[K]['body'][number] 369 | } 370 | > = MergeEnums 371 | 372 | type ParseGqlEnums< 373 | T extends string 374 | > = T extends `${infer _Whatever}enum ${infer EnumName}{${infer EnumBody}}${infer Rest}` 375 | ? [{ type: EnumName; body: EnumBody }, ...ParseGqlEnums] 376 | : [] 377 | 378 | type Map_ParseRawEnumString = T extends [] 379 | ? [] 380 | : // @ts-expect-error 381 | [ParseRawEnumString>, ...Map_ParseRawEnumString>] 382 | 383 | type ParseRawEnumString = { 384 | typeName: Trim 385 | // @ts-expect-error 386 | body: Filter_IsTextEmpty> 387 | } 388 | 389 | // --------------------------------- 390 | // ------- GQL Directives ---------- 391 | // --------------------------------- 392 | 393 | type ExtractGQLDirectivesAst< 394 | T extends string, 395 | RawDirectives extends { name: string; body: string }[] = ParseGqlDirectives, 396 | DirectivesObject = NormalizeArray, 397 | Merged = { 398 | // @ts-expect-error 399 | [K in keyof DirectivesObject]: ParseRawGQLValue 400 | } 401 | > = Merged 402 | 403 | type ParseGqlDirectives< 404 | T 405 | > = T extends `${infer _Whatever}\ndirective @${infer directiveName} on ${infer DirectiveBody}\n${infer Rest}` 406 | ? [ 407 | { 408 | name: directiveName 409 | body: DirectiveBody 410 | }, 411 | ...ParseGqlDirectives 412 | ] 413 | : [] 414 | 415 | // -------------------------------- 416 | // ---------- GQL Scalars ---------- 417 | // -------------------------------- 418 | 419 | type ExtractGQLScalarsAst< 420 | T extends string, 421 | RawDirectives extends { name: string }[] = ParseGqlScalar, 422 | ScalarsObject = NormalizeArray, 423 | Merged = { 424 | [K in keyof ScalarsObject]: any 425 | } 426 | > = Merged 427 | 428 | type ParseGqlScalar< 429 | T 430 | > = T extends `${infer _Whatever}\nscalar ${infer directiveName}\n${infer Rest}` 431 | ? [ 432 | { 433 | name: Trim 434 | }, 435 | ...ParseGqlScalar 436 | ] 437 | : [] 438 | 439 | // ---------------------------------- 440 | // ------------ main ---------------- 441 | // ---------------------------------- 442 | type GetGqlAST< 443 | T extends string, 444 | TTrimmedLines extends string = JoinArrByNewLine>>, 445 | ClearedCode extends string = RemoveGQLComments, 446 | GQLScalarsAST = ExtractGQLScalarsAst, 447 | GQLDirectivesAST = ExtractGQLDirectivesAst, 448 | GQLInputTypesAST = ExtractGQLInputTypesAst, 449 | GQLTypesAST = ExtractGQLTypesAST, 450 | GQLEnumsAST = ExtractGQLEnumsAST, 451 | GQLInterfacesAST = ExtractGQLInterfacesAST 452 | > = { 453 | scalars: GQLScalarsAST 454 | directives: GQLDirectivesAST 455 | types: GQLTypesAST 456 | enums: GQLEnumsAST 457 | inputs: GQLInputTypesAST 458 | interfaces: GQLInterfacesAST 459 | } 460 | 461 | const typeDefs = ` 462 | scalar Date1 463 | interface Node {id: ID!} 464 | # input CommentedPaginationPOC { input: Int! } 465 | input Pagination { value: String } 466 | enum OrderByKeyword {ASC,DESC} 467 | enum enum2 { A, B, C 468 | D} 469 | directive @upper on FIELD_DEFINITION 470 | type Mutation { 471 | contactForm(input: OrderByKeyword!): String 472 | } 473 | type Query implements Node & Node2 { 474 | age: Int, title( 475 | limit: Int! = 10 476 | offset: [Int!]!, thirdArg: String!): Mutation! 477 | author: Float! 478 | } 479 | ` 480 | 481 | type ParsedGraphQL = GetGqlAST 482 | 483 | type Enums = ParsedGraphQL['enums'] 484 | type Directives = ParsedGraphQL['directives'] 485 | type Scalars = ParsedGraphQL['scalars'] 486 | type Interfaces = ParsedGraphQL['interfaces'] 487 | type Types = ParsedGraphQL['types']['Query'] 488 | type Inputs = ParsedGraphQL['inputs'] 489 | 490 | --------------------------------------------------------------------------------