@gqlkit-ts/cli 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. package/dist/auto-type-generator/auto-type-generator.d.ts +26 -4
  2. package/dist/auto-type-generator/auto-type-generator.d.ts.map +1 -1
  3. package/dist/auto-type-generator/auto-type-generator.js +846 -152
  4. package/dist/auto-type-generator/auto-type-generator.js.map +1 -1
  5. package/dist/auto-type-generator/index.d.ts +9 -2
  6. package/dist/auto-type-generator/index.d.ts.map +1 -1
  7. package/dist/auto-type-generator/index.js +3 -0
  8. package/dist/auto-type-generator/index.js.map +1 -1
  9. package/dist/auto-type-generator/inline-enum-collector.d.ts +39 -0
  10. package/dist/auto-type-generator/inline-enum-collector.d.ts.map +1 -0
  11. package/dist/auto-type-generator/inline-enum-collector.js +193 -0
  12. package/dist/auto-type-generator/inline-enum-collector.js.map +1 -0
  13. package/dist/auto-type-generator/inline-object-traverser.d.ts +20 -0
  14. package/dist/auto-type-generator/inline-object-traverser.d.ts.map +1 -0
  15. package/dist/auto-type-generator/inline-object-traverser.js +22 -0
  16. package/dist/auto-type-generator/inline-object-traverser.js.map +1 -0
  17. package/dist/auto-type-generator/inline-union-collector.d.ts +29 -0
  18. package/dist/auto-type-generator/inline-union-collector.d.ts.map +1 -0
  19. package/dist/auto-type-generator/inline-union-collector.js +216 -0
  20. package/dist/auto-type-generator/inline-union-collector.js.map +1 -0
  21. package/dist/auto-type-generator/inline-union-types.d.ts +29 -0
  22. package/dist/auto-type-generator/inline-union-types.d.ts.map +1 -0
  23. package/dist/auto-type-generator/inline-union-types.js +2 -0
  24. package/dist/auto-type-generator/inline-union-types.js.map +1 -0
  25. package/dist/auto-type-generator/inline-union-validator.d.ts +76 -0
  26. package/dist/auto-type-generator/inline-union-validator.d.ts.map +1 -0
  27. package/dist/auto-type-generator/inline-union-validator.js +329 -0
  28. package/dist/auto-type-generator/inline-union-validator.js.map +1 -0
  29. package/dist/auto-type-generator/naming-convention.d.ts +22 -1
  30. package/dist/auto-type-generator/naming-convention.d.ts.map +1 -1
  31. package/dist/auto-type-generator/naming-convention.js +24 -0
  32. package/dist/auto-type-generator/naming-convention.js.map +1 -1
  33. package/dist/auto-type-generator/resolve-type-generator.d.ts +20 -0
  34. package/dist/auto-type-generator/resolve-type-generator.d.ts.map +1 -0
  35. package/dist/auto-type-generator/resolve-type-generator.js +2 -0
  36. package/dist/auto-type-generator/resolve-type-generator.js.map +1 -0
  37. package/dist/auto-type-generator/resolver-field-iterator.d.ts +13 -0
  38. package/dist/auto-type-generator/resolver-field-iterator.d.ts.map +1 -0
  39. package/dist/auto-type-generator/resolver-field-iterator.js +22 -0
  40. package/dist/auto-type-generator/resolver-field-iterator.js.map +1 -0
  41. package/dist/auto-type-generator/typename-extractor.d.ts +26 -0
  42. package/dist/auto-type-generator/typename-extractor.d.ts.map +1 -0
  43. package/dist/auto-type-generator/typename-extractor.js +142 -0
  44. package/dist/auto-type-generator/typename-extractor.js.map +1 -0
  45. package/dist/auto-type-generator/typename-resolve-type-generator.d.ts +35 -0
  46. package/dist/auto-type-generator/typename-resolve-type-generator.d.ts.map +1 -0
  47. package/dist/auto-type-generator/typename-resolve-type-generator.js +177 -0
  48. package/dist/auto-type-generator/typename-resolve-type-generator.js.map +1 -0
  49. package/dist/auto-type-generator/typename-types.d.ts +43 -0
  50. package/dist/auto-type-generator/typename-types.d.ts.map +1 -0
  51. package/dist/auto-type-generator/typename-types.js +37 -0
  52. package/dist/auto-type-generator/typename-types.js.map +1 -0
  53. package/dist/auto-type-generator/typename-validator.d.ts +37 -0
  54. package/dist/auto-type-generator/typename-validator.d.ts.map +1 -0
  55. package/dist/auto-type-generator/typename-validator.js +206 -0
  56. package/dist/auto-type-generator/typename-validator.js.map +1 -0
  57. package/dist/cli.js +2 -0
  58. package/dist/cli.js.map +1 -1
  59. package/dist/commands/docs.d.ts +51 -0
  60. package/dist/commands/docs.d.ts.map +1 -0
  61. package/dist/commands/docs.js +154 -0
  62. package/dist/commands/docs.js.map +1 -0
  63. package/dist/gen-orchestrator/orchestrator.d.ts.map +1 -1
  64. package/dist/gen-orchestrator/orchestrator.js +119 -14
  65. package/dist/gen-orchestrator/orchestrator.js.map +1 -1
  66. package/dist/resolver-extractor/extract-resolvers.d.ts +28 -1
  67. package/dist/resolver-extractor/extract-resolvers.d.ts.map +1 -1
  68. package/dist/resolver-extractor/extractor/define-api-extractor.d.ts +16 -1
  69. package/dist/resolver-extractor/extractor/define-api-extractor.d.ts.map +1 -1
  70. package/dist/resolver-extractor/extractor/define-api-extractor.js +86 -309
  71. package/dist/resolver-extractor/extractor/define-api-extractor.js.map +1 -1
  72. package/dist/resolver-extractor/index.d.ts +0 -1
  73. package/dist/resolver-extractor/index.d.ts.map +1 -1
  74. package/dist/resolver-extractor/validator/abstract-resolver-validator.d.ts +1 -0
  75. package/dist/resolver-extractor/validator/abstract-resolver-validator.d.ts.map +1 -1
  76. package/dist/resolver-extractor/validator/abstract-resolver-validator.js +9 -5
  77. package/dist/resolver-extractor/validator/abstract-resolver-validator.js.map +1 -1
  78. package/dist/schema-generator/emitter/code-emitter.d.ts +10 -2
  79. package/dist/schema-generator/emitter/code-emitter.d.ts.map +1 -1
  80. package/dist/schema-generator/emitter/code-emitter.js +39 -4
  81. package/dist/schema-generator/emitter/code-emitter.js.map +1 -1
  82. package/dist/schema-generator/generate-schema.d.ts +1 -0
  83. package/dist/schema-generator/generate-schema.d.ts.map +1 -1
  84. package/dist/schema-generator/generate-schema.js +90 -4
  85. package/dist/schema-generator/generate-schema.js.map +1 -1
  86. package/dist/schema-generator/integrator/result-integrator.d.ts +34 -2
  87. package/dist/schema-generator/integrator/result-integrator.d.ts.map +1 -1
  88. package/dist/schema-generator/integrator/result-integrator.js +96 -1
  89. package/dist/schema-generator/integrator/result-integrator.js.map +1 -1
  90. package/dist/schema-generator/resolver-collector/resolver-collector.d.ts +2 -0
  91. package/dist/schema-generator/resolver-collector/resolver-collector.d.ts.map +1 -1
  92. package/dist/schema-generator/resolver-collector/resolver-collector.js +22 -0
  93. package/dist/schema-generator/resolver-collector/resolver-collector.js.map +1 -1
  94. package/dist/shared/constants.d.ts +0 -16
  95. package/dist/shared/constants.d.ts.map +1 -1
  96. package/dist/shared/constants.js +0 -19
  97. package/dist/shared/constants.js.map +1 -1
  98. package/dist/shared/directive-detector.d.ts.map +1 -1
  99. package/dist/shared/directive-detector.js +8 -15
  100. package/dist/shared/directive-detector.js.map +1 -1
  101. package/dist/shared/enum-prefix-detector.d.ts +63 -0
  102. package/dist/shared/enum-prefix-detector.d.ts.map +1 -0
  103. package/dist/shared/enum-prefix-detector.js +80 -0
  104. package/dist/shared/enum-prefix-detector.js.map +1 -0
  105. package/dist/shared/file-scanner.d.ts.map +1 -1
  106. package/dist/shared/file-scanner.js +5 -3
  107. package/dist/shared/file-scanner.js.map +1 -1
  108. package/dist/shared/ignore-fields-detector.d.ts +26 -0
  109. package/dist/shared/ignore-fields-detector.d.ts.map +1 -0
  110. package/dist/shared/ignore-fields-detector.js +83 -0
  111. package/dist/shared/ignore-fields-detector.js.map +1 -0
  112. package/dist/shared/ignore-fields-validator.d.ts +29 -0
  113. package/dist/shared/ignore-fields-validator.d.ts.map +1 -0
  114. package/dist/shared/ignore-fields-validator.js +43 -0
  115. package/dist/shared/ignore-fields-validator.js.map +1 -0
  116. package/dist/shared/index.d.ts +3 -1
  117. package/dist/shared/index.d.ts.map +1 -1
  118. package/dist/shared/index.js +1 -3
  119. package/dist/shared/index.js.map +1 -1
  120. package/dist/shared/interface-detector.d.ts +3 -2
  121. package/dist/shared/interface-detector.d.ts.map +1 -1
  122. package/dist/shared/interface-detector.js +54 -11
  123. package/dist/shared/interface-detector.js.map +1 -1
  124. package/dist/shared/path-utils.d.ts +2 -0
  125. package/dist/shared/path-utils.d.ts.map +1 -0
  126. package/dist/shared/path-utils.js +4 -0
  127. package/dist/shared/path-utils.js.map +1 -0
  128. package/dist/shared/source-location.d.ts +5 -0
  129. package/dist/shared/source-location.d.ts.map +1 -1
  130. package/dist/shared/source-location.js +7 -0
  131. package/dist/shared/source-location.js.map +1 -1
  132. package/dist/shared/type-converter.d.ts.map +1 -1
  133. package/dist/shared/type-converter.js +11 -0
  134. package/dist/shared/type-converter.js.map +1 -1
  135. package/dist/shared/typescript-utils.d.ts +34 -7
  136. package/dist/shared/typescript-utils.d.ts.map +1 -1
  137. package/dist/shared/typescript-utils.js +72 -24
  138. package/dist/shared/typescript-utils.js.map +1 -1
  139. package/dist/type-extractor/collector/scalar-collector.d.ts.map +1 -1
  140. package/dist/type-extractor/collector/scalar-collector.js +4 -14
  141. package/dist/type-extractor/collector/scalar-collector.js.map +1 -1
  142. package/dist/type-extractor/converter/graphql-converter.d.ts.map +1 -1
  143. package/dist/type-extractor/converter/graphql-converter.js +57 -4
  144. package/dist/type-extractor/converter/graphql-converter.js.map +1 -1
  145. package/dist/type-extractor/extractor/field-type-resolver.d.ts +28 -0
  146. package/dist/type-extractor/extractor/field-type-resolver.d.ts.map +1 -0
  147. package/dist/type-extractor/extractor/field-type-resolver.js +433 -0
  148. package/dist/type-extractor/extractor/field-type-resolver.js.map +1 -0
  149. package/dist/type-extractor/extractor/type-extractor.d.ts +12 -3
  150. package/dist/type-extractor/extractor/type-extractor.d.ts.map +1 -1
  151. package/dist/type-extractor/extractor/type-extractor.js +260 -229
  152. package/dist/type-extractor/extractor/type-extractor.js.map +1 -1
  153. package/dist/type-extractor/extractor/type-name-collector.d.ts +24 -0
  154. package/dist/type-extractor/extractor/type-name-collector.d.ts.map +1 -0
  155. package/dist/type-extractor/extractor/type-name-collector.js +102 -0
  156. package/dist/type-extractor/extractor/type-name-collector.js.map +1 -0
  157. package/dist/type-extractor/mapper/scalar-base-type-mapper.d.ts +89 -0
  158. package/dist/type-extractor/mapper/scalar-base-type-mapper.d.ts.map +1 -0
  159. package/dist/type-extractor/mapper/scalar-base-type-mapper.js +158 -0
  160. package/dist/type-extractor/mapper/scalar-base-type-mapper.js.map +1 -0
  161. package/dist/type-extractor/types/diagnostics.d.ts +1 -1
  162. package/dist/type-extractor/types/diagnostics.d.ts.map +1 -1
  163. package/dist/type-extractor/types/graphql.d.ts +2 -0
  164. package/dist/type-extractor/types/graphql.d.ts.map +1 -1
  165. package/dist/type-extractor/types/index.d.ts +2 -1
  166. package/dist/type-extractor/types/index.d.ts.map +1 -1
  167. package/dist/type-extractor/types/index.js +1 -1
  168. package/dist/type-extractor/types/index.js.map +1 -1
  169. package/dist/type-extractor/types/ts-type-reference-factory.d.ts +47 -0
  170. package/dist/type-extractor/types/ts-type-reference-factory.d.ts.map +1 -0
  171. package/dist/type-extractor/types/ts-type-reference-factory.js +75 -0
  172. package/dist/type-extractor/types/ts-type-reference-factory.js.map +1 -0
  173. package/dist/type-extractor/types/typescript.d.ts +25 -1
  174. package/dist/type-extractor/types/typescript.d.ts.map +1 -1
  175. package/dist/type-extractor/validator/type-validator.js +1 -1
  176. package/dist/type-extractor/validator/type-validator.js.map +1 -1
  177. package/docs/coding-agents.md +64 -0
  178. package/docs/configuration.md +6 -20
  179. package/docs/getting-started.md +15 -12
  180. package/docs/index.md +36 -21
  181. package/docs/integration/apollo.md +8 -40
  182. package/docs/integration/drizzle.md +187 -0
  183. package/docs/integration/prisma.md +196 -0
  184. package/docs/integration/yoga.md +8 -40
  185. package/docs/schema/abstract-resolvers.md +117 -0
  186. package/docs/schema/directives.md +5 -0
  187. package/docs/schema/documentation.md +5 -0
  188. package/docs/schema/enums.md +307 -0
  189. package/docs/schema/fields.md +66 -0
  190. package/docs/schema/index.md +21 -0
  191. package/docs/schema/inputs.md +117 -15
  192. package/docs/schema/interfaces.md +31 -1
  193. package/docs/schema/objects.md +42 -0
  194. package/docs/schema/queries-mutations.md +138 -22
  195. package/docs/schema/scalars.md +5 -0
  196. package/docs/schema/unions.md +208 -1
  197. package/docs/what-is-gqlkit.md +13 -8
  198. package/package.json +14 -5
  199. package/src/auto-type-generator/auto-type-generator.ts +1670 -0
  200. package/src/auto-type-generator/index.ts +63 -0
  201. package/src/auto-type-generator/inline-enum-collector.ts +338 -0
  202. package/src/auto-type-generator/inline-object-traverser.ts +49 -0
  203. package/src/auto-type-generator/inline-union-collector.ts +402 -0
  204. package/src/auto-type-generator/inline-union-types.ts +33 -0
  205. package/src/auto-type-generator/inline-union-validator.ts +482 -0
  206. package/src/auto-type-generator/name-collision-validator.ts +119 -0
  207. package/src/auto-type-generator/naming-convention.ts +163 -0
  208. package/src/auto-type-generator/resolve-type-generator.ts +21 -0
  209. package/src/auto-type-generator/resolver-field-iterator.ts +39 -0
  210. package/src/auto-type-generator/typename-extractor.ts +230 -0
  211. package/src/auto-type-generator/typename-resolve-type-generator.ts +281 -0
  212. package/src/auto-type-generator/typename-types.ts +66 -0
  213. package/src/auto-type-generator/typename-validator.ts +326 -0
  214. package/src/cli.ts +13 -0
  215. package/src/commands/docs.ts +211 -0
  216. package/src/commands/gen.ts +141 -0
  217. package/src/commands/main.ts +5 -0
  218. package/src/config/define-config.ts +28 -0
  219. package/src/config/index.ts +7 -0
  220. package/src/config/types.ts +144 -0
  221. package/src/config-loader/index.ts +14 -0
  222. package/src/config-loader/loader.ts +143 -0
  223. package/src/config-loader/validator.ts +672 -0
  224. package/src/gen-orchestrator/hook-executor/hook-executor.ts +117 -0
  225. package/src/gen-orchestrator/orchestrator.ts +798 -0
  226. package/src/gen-orchestrator/reporter/diagnostic-reporter.ts +44 -0
  227. package/src/gen-orchestrator/reporter/progress-reporter.ts +61 -0
  228. package/src/gen-orchestrator/writer/file-writer.ts +38 -0
  229. package/src/index.ts +2 -0
  230. package/src/resolver-extractor/extract-resolvers.ts +82 -0
  231. package/src/resolver-extractor/extractor/define-api-extractor.ts +740 -0
  232. package/src/resolver-extractor/index.ts +13 -0
  233. package/src/resolver-extractor/validator/abstract-resolver-validator.ts +259 -0
  234. package/src/schema-generator/builder/ast-builder.ts +706 -0
  235. package/src/schema-generator/emitter/code-emitter.ts +385 -0
  236. package/src/schema-generator/emitter/sdl-emitter.ts +13 -0
  237. package/src/schema-generator/generate-schema.ts +267 -0
  238. package/src/schema-generator/index.ts +19 -0
  239. package/src/schema-generator/integrator/result-integrator.ts +759 -0
  240. package/src/schema-generator/pruner/schema-pruner.ts +112 -0
  241. package/src/schema-generator/resolver-collector/resolver-collector.ts +157 -0
  242. package/src/shared/constants.ts +122 -0
  243. package/src/shared/default-value-detector.ts +172 -0
  244. package/src/shared/diagnostics.ts +35 -0
  245. package/src/shared/directive-definition-extractor.ts +564 -0
  246. package/src/shared/directive-detector.ts +556 -0
  247. package/src/shared/enum-prefix-detector.ts +99 -0
  248. package/src/shared/file-scanner.ts +170 -0
  249. package/src/shared/ignore-fields-detector.ts +109 -0
  250. package/src/shared/ignore-fields-validator.ts +66 -0
  251. package/src/shared/index.ts +34 -0
  252. package/src/shared/inline-object-extractor.ts +102 -0
  253. package/src/shared/inline-object-utils.ts +23 -0
  254. package/src/shared/interface-detector.ts +176 -0
  255. package/src/shared/interface-validator.ts +211 -0
  256. package/src/shared/metadata-detector.ts +443 -0
  257. package/src/shared/path-utils.ts +3 -0
  258. package/src/shared/program-factory.ts +51 -0
  259. package/src/shared/source-location.ts +38 -0
  260. package/src/shared/tsconfig-loader.ts +126 -0
  261. package/src/shared/tsdoc-parser.ts +155 -0
  262. package/src/shared/type-converter.ts +99 -0
  263. package/src/shared/typescript-utils.ts +246 -0
  264. package/src/type-extractor/collector/result-collector.ts +57 -0
  265. package/src/type-extractor/collector/scalar-collector.ts +254 -0
  266. package/src/type-extractor/converter/field-eligibility.ts +112 -0
  267. package/src/type-extractor/converter/graphql-converter.ts +483 -0
  268. package/src/type-extractor/extract-types.ts +1 -0
  269. package/src/type-extractor/extractor/field-type-resolver.ts +614 -0
  270. package/src/type-extractor/extractor/type-extractor.ts +1644 -0
  271. package/src/type-extractor/extractor/type-name-collector.ts +130 -0
  272. package/src/type-extractor/index.ts +20 -0
  273. package/src/type-extractor/mapper/scalar-base-type-mapper.ts +265 -0
  274. package/src/type-extractor/types/diagnostics.ts +109 -0
  275. package/src/type-extractor/types/graphql.ts +55 -0
  276. package/src/type-extractor/types/index.ts +37 -0
  277. package/src/type-extractor/types/ts-type-reference-factory.ts +150 -0
  278. package/src/type-extractor/types/typescript.ts +137 -0
  279. package/src/type-extractor/validator/type-validator.ts +77 -0
  280. package/dist/auto-type-generator/auto-type-generator.test.d.ts +0 -2
  281. package/dist/auto-type-generator/auto-type-generator.test.d.ts.map +0 -1
  282. package/dist/auto-type-generator/auto-type-generator.test.js +0 -613
  283. package/dist/auto-type-generator/auto-type-generator.test.js.map +0 -1
  284. package/dist/auto-type-generator/name-collision-validator.test.d.ts +0 -2
  285. package/dist/auto-type-generator/name-collision-validator.test.d.ts.map +0 -1
  286. package/dist/auto-type-generator/name-collision-validator.test.js +0 -358
  287. package/dist/auto-type-generator/name-collision-validator.test.js.map +0 -1
  288. package/dist/auto-type-generator/naming-convention.test.d.ts +0 -2
  289. package/dist/auto-type-generator/naming-convention.test.d.ts.map +0 -1
  290. package/dist/auto-type-generator/naming-convention.test.js +0 -132
  291. package/dist/auto-type-generator/naming-convention.test.js.map +0 -1
  292. package/dist/commands/gen.test.d.ts +0 -2
  293. package/dist/commands/gen.test.d.ts.map +0 -1
  294. package/dist/commands/gen.test.js +0 -226
  295. package/dist/commands/gen.test.js.map +0 -1
  296. package/dist/config-loader/loader.test.d.ts +0 -2
  297. package/dist/config-loader/loader.test.d.ts.map +0 -1
  298. package/dist/config-loader/loader.test.js +0 -123
  299. package/dist/config-loader/loader.test.js.map +0 -1
  300. package/dist/config-loader/validator.test.d.ts +0 -2
  301. package/dist/config-loader/validator.test.d.ts.map +0 -1
  302. package/dist/config-loader/validator.test.js +0 -846
  303. package/dist/config-loader/validator.test.js.map +0 -1
  304. package/dist/gen-orchestrator/golden.test.d.ts +0 -2
  305. package/dist/gen-orchestrator/golden.test.d.ts.map +0 -1
  306. package/dist/gen-orchestrator/golden.test.js +0 -102
  307. package/dist/gen-orchestrator/golden.test.js.map +0 -1
  308. package/dist/gen-orchestrator/hook-executor/hook-executor.test.d.ts +0 -2
  309. package/dist/gen-orchestrator/hook-executor/hook-executor.test.d.ts.map +0 -1
  310. package/dist/gen-orchestrator/hook-executor/hook-executor.test.js +0 -167
  311. package/dist/gen-orchestrator/hook-executor/hook-executor.test.js.map +0 -1
  312. package/dist/gen-orchestrator/reporter/progress-reporter.test.d.ts +0 -2
  313. package/dist/gen-orchestrator/reporter/progress-reporter.test.d.ts.map +0 -1
  314. package/dist/gen-orchestrator/reporter/progress-reporter.test.js +0 -74
  315. package/dist/gen-orchestrator/reporter/progress-reporter.test.js.map +0 -1
  316. package/dist/resolver-extractor/validator/only-validator.d.ts +0 -61
  317. package/dist/resolver-extractor/validator/only-validator.d.ts.map +0 -1
  318. package/dist/resolver-extractor/validator/only-validator.js +0 -76
  319. package/dist/resolver-extractor/validator/only-validator.js.map +0 -1
  320. package/dist/resolver-extractor/validator/only-validator.test.d.ts +0 -8
  321. package/dist/resolver-extractor/validator/only-validator.test.d.ts.map +0 -1
  322. package/dist/resolver-extractor/validator/only-validator.test.js +0 -352
  323. package/dist/resolver-extractor/validator/only-validator.test.js.map +0 -1
  324. package/dist/schema-generator/builder/ast-builder.test.d.ts +0 -2
  325. package/dist/schema-generator/builder/ast-builder.test.d.ts.map +0 -1
  326. package/dist/schema-generator/builder/ast-builder.test.js +0 -469
  327. package/dist/schema-generator/builder/ast-builder.test.js.map +0 -1
  328. package/dist/shared/file-scanner.test.d.ts +0 -2
  329. package/dist/shared/file-scanner.test.d.ts.map +0 -1
  330. package/dist/shared/file-scanner.test.js +0 -138
  331. package/dist/shared/file-scanner.test.js.map +0 -1
  332. package/dist/shared/interface-validator.test.d.ts +0 -2
  333. package/dist/shared/interface-validator.test.d.ts.map +0 -1
  334. package/dist/shared/interface-validator.test.js +0 -145
  335. package/dist/shared/interface-validator.test.js.map +0 -1
  336. package/dist/type-extractor/types/typescript.test.d.ts +0 -2
  337. package/dist/type-extractor/types/typescript.test.d.ts.map +0 -1
  338. package/dist/type-extractor/types/typescript.test.js +0 -287
  339. package/dist/type-extractor/types/typescript.test.js.map +0 -1
@@ -0,0 +1,1644 @@
1
+ import { resolve } from "node:path";
2
+ import ts from "typescript";
3
+ import { isInternalTypeSymbol } from "../../shared/constants.js";
4
+ import { detectDefaultValueMetadata } from "../../shared/default-value-detector.js";
5
+ import {
6
+ type DirectiveArgumentValue,
7
+ type DirectiveInfo,
8
+ detectDirectiveMetadata,
9
+ hasDirectiveMetadata,
10
+ unwrapDirectiveType,
11
+ } from "../../shared/directive-detector.js";
12
+ import { detectIgnoreFieldsMetadata } from "../../shared/ignore-fields-detector.js";
13
+ import { validateIgnoreFields } from "../../shared/ignore-fields-validator.js";
14
+ import { extractInlineObjectProperties as extractInlineObjectPropertiesShared } from "../../shared/inline-object-extractor.js";
15
+ import { isInlineObjectType } from "../../shared/inline-object-utils.js";
16
+ import {
17
+ extractImplementsFromDefineInterface,
18
+ extractImplementsFromGqlTypeDef,
19
+ isDefineInterfaceTypeAlias,
20
+ } from "../../shared/interface-detector.js";
21
+ import { detectScalarMetadata } from "../../shared/metadata-detector.js";
22
+ import {
23
+ getSourceLocationFromNode,
24
+ type SourceLocation,
25
+ } from "../../shared/source-location.js";
26
+ import {
27
+ extractTsDocFromSymbol,
28
+ extractTsDocInfo,
29
+ } from "../../shared/tsdoc-parser.js";
30
+ import {
31
+ extractPropertySymbols,
32
+ filterNonNullTypeNodes,
33
+ findEnumParentSymbol,
34
+ findNonNullTypeNode,
35
+ getNonNullableTypes,
36
+ getTypeNameFromNode,
37
+ hasUndefinedInType,
38
+ isAnonymousObjectType,
39
+ isBooleanUnion,
40
+ isExported,
41
+ isNullableUnion,
42
+ isNullOrUndefined,
43
+ shouldTreatIntersectionAsInline,
44
+ } from "../../shared/typescript-utils.js";
45
+ import type { ScalarMetadataInfo } from "../collector/scalar-collector.js";
46
+ import type {
47
+ ScalarBaseTypeMappingTable,
48
+ ScalarMappingContext,
49
+ } from "../mapper/scalar-base-type-mapper.js";
50
+ import {
51
+ createArrayType,
52
+ createInlineObjectType,
53
+ createLiteralType,
54
+ createPrimitiveType,
55
+ createReferenceType,
56
+ createScalarType,
57
+ createUnionType,
58
+ type Diagnostic,
59
+ type EnumMemberInfo,
60
+ type ExtractedTypeInfo,
61
+ type FieldDefinition,
62
+ type InlineObjectMember,
63
+ type InlineObjectProperty,
64
+ type TSTypeReference,
65
+ type TypeKind,
66
+ type TypeMetadata,
67
+ } from "../types/index.js";
68
+ import { resolveFieldType } from "./field-type-resolver.js";
69
+
70
+ /**
71
+ * Global type mapping configuration.
72
+ * Maps TypeScript type names to GraphQL scalar names when tsType.from is omitted.
73
+ */
74
+ export interface GlobalTypeMapping {
75
+ /** TypeScript type name (e.g., "Date", "URL") */
76
+ readonly typeName: string;
77
+ /** GraphQL scalar name (e.g., "DateTime", "URL") */
78
+ readonly scalarName: string;
79
+ /** Usage constraint */
80
+ readonly only: "input" | "output" | null;
81
+ }
82
+
83
+ export interface ExtractionOptions {
84
+ /** Global type mappings from config (scalars with tsType.from omitted) */
85
+ readonly globalTypeMappings: ReadonlyArray<GlobalTypeMapping>;
86
+ /** Set of type names declared in the schema (from Phase 1 collection) */
87
+ readonly knownTypeNames: ReadonlySet<string>;
88
+ /** Map of type names to their symbols (from Phase 1 collection) */
89
+ readonly knownTypeSymbols: ReadonlyMap<string, ts.Symbol>;
90
+ /** Map of underlying symbols to schema type names (for type alias resolution) */
91
+ readonly underlyingSymbolToTypeName: ReadonlyMap<ts.Symbol, string>;
92
+ /** Scalar base type mapping table for automatic base type -> scalar mapping */
93
+ readonly scalarMappingTable: ScalarBaseTypeMappingTable | null;
94
+ }
95
+
96
+ export interface ExtractionResult {
97
+ readonly types: ReadonlyArray<ExtractedTypeInfo>;
98
+ readonly diagnostics: ReadonlyArray<Diagnostic>;
99
+ readonly detectedScalarNames: ReadonlyArray<string>;
100
+ readonly detectedScalars: ReadonlyArray<ScalarMetadataInfo>;
101
+ }
102
+
103
+ function isDefaultExport(node: ts.Node, sourceFile: ts.SourceFile): boolean {
104
+ let hasDefaultExport = false;
105
+ const nodeName = (node as ts.DeclarationStatement).name?.getText(sourceFile);
106
+
107
+ ts.forEachChild(sourceFile, (child) => {
108
+ if (
109
+ ts.isExportAssignment(child) &&
110
+ !child.isExportEquals &&
111
+ ts.isIdentifier(child.expression)
112
+ ) {
113
+ if (child.expression.text === nodeName) {
114
+ hasDefaultExport = true;
115
+ }
116
+ }
117
+ });
118
+
119
+ return hasDefaultExport;
120
+ }
121
+
122
+ interface TypeReferenceResult {
123
+ readonly tsType: TSTypeReference;
124
+ }
125
+
126
+ /**
127
+ * Context for type declaration resolution.
128
+ * Used when processing type declarations (not field types).
129
+ */
130
+ interface TypeDeclarationContext {
131
+ readonly checker: ts.TypeChecker;
132
+ readonly globalTypeMappings: ReadonlyArray<GlobalTypeMapping>;
133
+ readonly knownTypeNames: ReadonlySet<string>;
134
+ readonly visitedTypes: WeakSet<ts.Type>;
135
+ }
136
+
137
+ /**
138
+ * Attempts to extract a type as an inline object, with cycle detection.
139
+ * Returns a reference type if a cycle is detected, otherwise returns an inline object.
140
+ */
141
+ function tryExtractAsInlineObject(
142
+ type: ts.Type,
143
+ ctx: TypeDeclarationContext,
144
+ ): TypeReferenceResult {
145
+ const { checker, visitedTypes } = ctx;
146
+ if (visitedTypes.has(type)) {
147
+ const typeName = type.symbol?.getName() ?? "Object";
148
+ return {
149
+ tsType: createReferenceType({
150
+ name: typeName === "__type" ? "Object" : typeName,
151
+ nullable: false,
152
+ }),
153
+ };
154
+ }
155
+ visitedTypes.add(type);
156
+ const inlineProperties = extractInlineObjectPropertiesShared(
157
+ type,
158
+ checker,
159
+ (t) => convertTsTypeToReference(t, ctx).tsType,
160
+ );
161
+ return {
162
+ tsType: createInlineObjectType({
163
+ properties: inlineProperties,
164
+ description: null,
165
+ deprecated: null,
166
+ }),
167
+ };
168
+ }
169
+
170
+ function findGlobalTypeMapping(
171
+ typeName: string,
172
+ globalTypeMappings: ReadonlyArray<GlobalTypeMapping>,
173
+ ): GlobalTypeMapping | undefined {
174
+ return globalTypeMappings.find((m) => m.typeName === typeName);
175
+ }
176
+
177
+ function convertTsTypeToReference(
178
+ type: ts.Type,
179
+ ctx: TypeDeclarationContext,
180
+ typeNode?: ts.TypeNode,
181
+ ): TypeReferenceResult {
182
+ const { checker, globalTypeMappings, knownTypeNames } = ctx;
183
+ const metadataResult = detectScalarMetadata(type, checker);
184
+ // Skip scalar detection if it's an array of scalars (e.g., Int[])
185
+ // Array types should be handled by the array handling logic below
186
+ if (
187
+ metadataResult.scalarName &&
188
+ !metadataResult.isPrimitive &&
189
+ !metadataResult.isList
190
+ ) {
191
+ return {
192
+ tsType: createScalarType({
193
+ name: metadataResult.scalarName,
194
+ scalarInfo: {
195
+ scalarName: metadataResult.scalarName,
196
+ typeName: metadataResult.scalarName,
197
+ baseType: undefined,
198
+ isCustom: true,
199
+ only: metadataResult.only,
200
+ },
201
+ nullable: metadataResult.nullable,
202
+ }),
203
+ };
204
+ }
205
+
206
+ if (isBooleanUnion(type)) {
207
+ const nullable = isNullableUnion(type);
208
+ return {
209
+ tsType: createPrimitiveType({ name: "boolean", nullable }),
210
+ };
211
+ }
212
+
213
+ if (type.isUnion()) {
214
+ const nullable = isNullableUnion(type);
215
+
216
+ // Preserve type alias name for enum types (string literal unions)
217
+ const aliasSymbol = type.aliasSymbol;
218
+ if (aliasSymbol) {
219
+ const name = aliasSymbol.getName();
220
+ return {
221
+ tsType: createReferenceType({ name, nullable }),
222
+ };
223
+ }
224
+
225
+ const nonNullTypes = getNonNullableTypes(type);
226
+
227
+ // Check if all non-null types belong to the same enum (for numeric enums)
228
+ const enumParentSymbol = findEnumParentSymbol(nonNullTypes);
229
+ if (enumParentSymbol) {
230
+ return {
231
+ tsType: createReferenceType({
232
+ name: enumParentSymbol.getName(),
233
+ nullable,
234
+ }),
235
+ };
236
+ }
237
+
238
+ if (nonNullTypes.length === 1) {
239
+ // For nullable types like User | null, extract the non-null type node
240
+ const nonNullTypeNode =
241
+ typeNode && ts.isUnionTypeNode(typeNode)
242
+ ? findNonNullTypeNode(typeNode)
243
+ : undefined;
244
+
245
+ const innerResult = convertTsTypeToReference(
246
+ nonNullTypes[0]!,
247
+ ctx,
248
+ nonNullTypeNode,
249
+ );
250
+ return {
251
+ tsType: { ...innerResult.tsType, nullable },
252
+ };
253
+ }
254
+
255
+ const memberResults = nonNullTypes.map((t) =>
256
+ convertTsTypeToReference(t, ctx),
257
+ );
258
+
259
+ return {
260
+ tsType: createUnionType({
261
+ members: memberResults.map((r) => r.tsType),
262
+ nullable,
263
+ }),
264
+ };
265
+ }
266
+
267
+ if (checker.isArrayType(type)) {
268
+ const typeArgs = (type as ts.TypeReference).typeArguments;
269
+ const elementType = typeArgs?.[0];
270
+
271
+ // Extract element type node from array type node (e.g., User[] -> User)
272
+ let elementTypeNode: ts.TypeNode | undefined;
273
+ if (typeNode && ts.isArrayTypeNode(typeNode)) {
274
+ elementTypeNode = typeNode.elementType;
275
+ }
276
+
277
+ const elementResult = elementType
278
+ ? convertTsTypeToReference(elementType, ctx, elementTypeNode)
279
+ : {
280
+ tsType: createPrimitiveType({ name: "unknown", nullable: false }),
281
+ };
282
+
283
+ return {
284
+ tsType: createArrayType(elementResult.tsType),
285
+ };
286
+ }
287
+
288
+ const typeString = checker.typeToString(type);
289
+
290
+ if (type.flags & ts.TypeFlags.String) {
291
+ return {
292
+ tsType: createPrimitiveType({ name: "string", nullable: false }),
293
+ };
294
+ }
295
+ if (type.flags & ts.TypeFlags.Number) {
296
+ return {
297
+ tsType: createPrimitiveType({ name: "number", nullable: false }),
298
+ };
299
+ }
300
+ if (
301
+ type.flags & ts.TypeFlags.Boolean ||
302
+ type.flags & ts.TypeFlags.BooleanLiteral
303
+ ) {
304
+ return {
305
+ tsType: createPrimitiveType({ name: "boolean", nullable: false }),
306
+ };
307
+ }
308
+ if (type.flags & ts.TypeFlags.StringLiteral) {
309
+ return {
310
+ tsType: createLiteralType(typeString.replace(/"/g, "")),
311
+ };
312
+ }
313
+ if (type.flags & ts.TypeFlags.NumberLiteral) {
314
+ return {
315
+ tsType: createLiteralType(typeString),
316
+ };
317
+ }
318
+
319
+ // Handle intersection types that should be treated as inline objects
320
+ // This includes intersections with anonymous members OR intersections of
321
+ // named object types (interfaces) that are not exported as GraphQL types
322
+ if (type.isIntersection()) {
323
+ // If the intersection type has an alias symbol (e.g., Comment = GqlObject<...>),
324
+ // treat it as a named reference to avoid infinite recursion with self-referential types
325
+ if (type.aliasSymbol) {
326
+ const aliasName = type.aliasSymbol.getName();
327
+ return {
328
+ tsType: createReferenceType({ name: aliasName, nullable: false }),
329
+ };
330
+ }
331
+
332
+ const shouldTreatAsInline = shouldTreatIntersectionAsInline(type);
333
+ if (shouldTreatAsInline) {
334
+ return tryExtractAsInlineObject(type, ctx);
335
+ }
336
+ }
337
+
338
+ if (isInlineObjectType(type)) {
339
+ // Check if typeNode references a known type (schema-defined type)
340
+ if (typeNode && ts.isTypeReferenceNode(typeNode)) {
341
+ const typeName = getTypeNameFromNode(typeNode);
342
+ if (typeName && knownTypeNames.has(typeName)) {
343
+ return {
344
+ tsType: createReferenceType({ name: typeName, nullable: false }),
345
+ };
346
+ }
347
+ }
348
+
349
+ return tryExtractAsInlineObject(type, ctx);
350
+ }
351
+
352
+ // Check for utility types (Omit, Pick, Partial, Required, etc.)
353
+ // These create mapped types that should be treated as inline objects
354
+ if (type.flags & ts.TypeFlags.Object) {
355
+ const objectType = type as ts.ObjectType;
356
+ if (objectType.objectFlags & ts.ObjectFlags.Mapped) {
357
+ // Check if typeNode references a known type (schema-defined type).
358
+ // This handles Simplify<T> = { [K in keyof T]: T[K] } & {} pattern.
359
+ if (typeNode && ts.isTypeReferenceNode(typeNode)) {
360
+ const typeName = getTypeNameFromNode(typeNode);
361
+ // Only use typeNode name if it's in knownTypeNames (schema-defined type)
362
+ if (typeName && knownTypeNames.has(typeName)) {
363
+ return {
364
+ tsType: createReferenceType({ name: typeName, nullable: false }),
365
+ };
366
+ }
367
+ }
368
+ return tryExtractAsInlineObject(type, ctx);
369
+ }
370
+ }
371
+
372
+ if (type.symbol) {
373
+ const symbolName = type.symbol.getName();
374
+
375
+ // Skip internal TypeScript symbols (see constants.ts for details)
376
+ if (!isInternalTypeSymbol(symbolName)) {
377
+ const globalMapping = findGlobalTypeMapping(
378
+ symbolName,
379
+ globalTypeMappings,
380
+ );
381
+ if (globalMapping) {
382
+ return {
383
+ tsType: createScalarType({
384
+ name: globalMapping.scalarName,
385
+ scalarInfo: {
386
+ scalarName: globalMapping.scalarName,
387
+ typeName: globalMapping.typeName,
388
+ baseType: undefined,
389
+ isCustom: true,
390
+ only: globalMapping.only,
391
+ },
392
+ nullable: false,
393
+ }),
394
+ };
395
+ }
396
+
397
+ return {
398
+ tsType: createReferenceType({ name: symbolName, nullable: false }),
399
+ };
400
+ }
401
+ }
402
+
403
+ return {
404
+ tsType: createReferenceType({ name: typeString, nullable: false }),
405
+ };
406
+ }
407
+
408
+ interface FieldExtractionResult {
409
+ fields: FieldDefinition[];
410
+ diagnostics: Diagnostic[];
411
+ }
412
+
413
+ function collectAllFieldNames(
414
+ type: ts.Type,
415
+ checker: ts.TypeChecker,
416
+ ): ReadonlySet<string> {
417
+ const properties = extractPropertySymbols(type, checker);
418
+ const fieldNames = new Set<string>();
419
+ for (const prop of properties) {
420
+ const propName = prop.getName();
421
+ if (!propName.startsWith(" $")) {
422
+ fieldNames.add(propName);
423
+ }
424
+ }
425
+ return fieldNames;
426
+ }
427
+
428
+ interface ExtractFieldsParams {
429
+ readonly type: ts.Type;
430
+ readonly checker: ts.TypeChecker;
431
+ readonly globalTypeMappings: ReadonlyArray<GlobalTypeMapping>;
432
+ readonly knownTypeNames: ReadonlySet<string>;
433
+ readonly knownTypeSymbols: ReadonlyMap<string, ts.Symbol>;
434
+ readonly underlyingSymbolToTypeName: ReadonlyMap<ts.Symbol, string>;
435
+ readonly sourceFiles: ReadonlySet<string>;
436
+ readonly scalarMappingTable: ScalarBaseTypeMappingTable | null;
437
+ readonly scalarMappingContext: ScalarMappingContext;
438
+ readonly ignoreFields: ReadonlySet<string> | null;
439
+ }
440
+
441
+ function extractFieldsFromType(
442
+ params: ExtractFieldsParams,
443
+ ): FieldExtractionResult {
444
+ const {
445
+ type,
446
+ checker,
447
+ globalTypeMappings,
448
+ knownTypeNames,
449
+ knownTypeSymbols,
450
+ underlyingSymbolToTypeName,
451
+ sourceFiles,
452
+ scalarMappingTable,
453
+ scalarMappingContext,
454
+ ignoreFields,
455
+ } = params;
456
+ const fields: FieldDefinition[] = [];
457
+ const diagnostics: Diagnostic[] = [];
458
+ const properties = extractPropertySymbols(type, checker);
459
+
460
+ for (const prop of properties) {
461
+ const propName = prop.getName();
462
+
463
+ if (propName.startsWith(" $")) {
464
+ continue;
465
+ }
466
+
467
+ if (ignoreFields?.has(propName)) {
468
+ continue;
469
+ }
470
+
471
+ const propType = checker.getTypeOfSymbol(prop);
472
+ const declarations = prop.getDeclarations();
473
+ const declaration = declarations?.[0];
474
+
475
+ const optional = hasUndefinedInType(propType);
476
+
477
+ const tsdocInfo = extractTsDocFromSymbol(prop, checker);
478
+
479
+ let actualPropType = propType;
480
+ let directives: ReadonlyArray<DirectiveInfo> | null = null;
481
+ let directiveNullable = false;
482
+ let defaultValue: DirectiveArgumentValue | null = null;
483
+
484
+ if (hasDirectiveMetadata(propType)) {
485
+ const directiveResult = detectDirectiveMetadata(propType, checker);
486
+ if (directiveResult.directives.length > 0) {
487
+ directives = directiveResult.directives;
488
+ }
489
+
490
+ // Detect default value from $gqlkitFieldMeta
491
+ const defaultValueResult = detectDefaultValueMetadata(propType, checker);
492
+ if (defaultValueResult.defaultValue) {
493
+ defaultValue = defaultValueResult.defaultValue;
494
+ }
495
+ if (defaultValueResult.errors.length > 0) {
496
+ for (const error of defaultValueResult.errors) {
497
+ diagnostics.push({
498
+ code: error.code,
499
+ message: `Field '${propName}': ${error.message}`,
500
+ severity: "warning",
501
+ location: getSourceLocationFromNode(declaration),
502
+ });
503
+ }
504
+ }
505
+
506
+ // Check if the original type is nullable before unwrapping
507
+ // TypeScript normalizes WithDirectives<T | null, [...]> to (T & Directive) | null
508
+ if (isNullableUnion(propType)) {
509
+ directiveNullable = true;
510
+ }
511
+ actualPropType = unwrapDirectiveType(propType, checker);
512
+
513
+ // Check if the unwrapped type (from $gqlkitOriginalType) is nullable
514
+ // This handles cases where TypeScript normalizes intersection types
515
+ // and loses the null from the outer union
516
+ if (!directiveNullable && isNullableUnion(actualPropType)) {
517
+ directiveNullable = true;
518
+ }
519
+ }
520
+
521
+ // Get typeNode from property declaration to preserve type alias names
522
+ let propTypeNode: ts.TypeNode | undefined;
523
+ if (
524
+ declaration &&
525
+ (ts.isPropertySignature(declaration) ||
526
+ ts.isPropertyDeclaration(declaration))
527
+ ) {
528
+ propTypeNode = declaration.type;
529
+ }
530
+
531
+ const resolvedType = resolveFieldType(actualPropType, propTypeNode, {
532
+ checker,
533
+ knownTypeNames,
534
+ knownTypeSymbols,
535
+ underlyingSymbolToTypeName,
536
+ globalTypeMappings,
537
+ sourceFiles,
538
+ scalarMappingTable,
539
+ scalarMappingContext,
540
+ });
541
+
542
+ // Preserve nullability from original WithDirectives type
543
+ const tsType =
544
+ directiveNullable && !resolvedType.nullable
545
+ ? { ...resolvedType, nullable: true }
546
+ : resolvedType;
547
+
548
+ fields.push({
549
+ name: propName,
550
+ tsType,
551
+ optional,
552
+ description: tsdocInfo.description ?? null,
553
+ deprecated: tsdocInfo.deprecated ?? null,
554
+ directives,
555
+ defaultValue,
556
+ sourceLocation: getSourceLocationFromNode(declaration),
557
+ });
558
+ }
559
+
560
+ return { fields, diagnostics };
561
+ }
562
+
563
+ function isHeterogeneousEnum(node: ts.Node): boolean {
564
+ if (!ts.isEnumDeclaration(node)) return false;
565
+ const members = node.members;
566
+ if (members.length <= 1) return false;
567
+
568
+ let hasString = false;
569
+ let hasNumeric = false;
570
+
571
+ for (const member of members) {
572
+ const initializer = member.initializer;
573
+ if (initializer === undefined) {
574
+ hasNumeric = true;
575
+ } else if (ts.isStringLiteral(initializer)) {
576
+ hasString = true;
577
+ } else if (
578
+ ts.isNumericLiteral(initializer) ||
579
+ ts.isPrefixUnaryExpression(initializer)
580
+ ) {
581
+ hasNumeric = true;
582
+ }
583
+
584
+ if (hasString && hasNumeric) return true;
585
+ }
586
+
587
+ return false;
588
+ }
589
+
590
+ function isConstEnum(node: ts.Node): boolean {
591
+ if (!ts.isEnumDeclaration(node)) return false;
592
+ const modifiers = ts.getCombinedModifierFlags(node);
593
+ return (modifiers & ts.ModifierFlags.Const) !== 0;
594
+ }
595
+
596
+ function isStringLiteralUnion(type: ts.Type): boolean {
597
+ if (!type.isUnion()) return false;
598
+
599
+ const nonNullTypes = getNonNullableTypes(type);
600
+
601
+ if (nonNullTypes.length === 0) return false;
602
+
603
+ return nonNullTypes.every((t) => t.flags & ts.TypeFlags.StringLiteral);
604
+ }
605
+
606
+ function getEnumMemberName(memberName: ts.PropertyName): string {
607
+ if (ts.isIdentifier(memberName) || ts.isStringLiteral(memberName)) {
608
+ return memberName.text;
609
+ }
610
+ return memberName.getText();
611
+ }
612
+
613
+ function extractEnumMembers(
614
+ node: ts.EnumDeclaration,
615
+ checker: ts.TypeChecker,
616
+ ): ReadonlyArray<EnumMemberInfo> {
617
+ const members: EnumMemberInfo[] = [];
618
+
619
+ for (const member of node.members) {
620
+ const name = getEnumMemberName(member.name);
621
+ const initializer = member.initializer;
622
+
623
+ const symbol = checker.getSymbolAtLocation(member.name);
624
+ const tsdocInfo = symbol
625
+ ? extractTsDocFromSymbol(symbol, checker)
626
+ : { description: undefined, deprecated: undefined };
627
+
628
+ if (initializer && ts.isStringLiteral(initializer)) {
629
+ members.push({
630
+ name,
631
+ value: initializer.text,
632
+ numericValue: null,
633
+ description: tsdocInfo.description ?? null,
634
+ deprecated: tsdocInfo.deprecated ?? null,
635
+ sourceLocation: getSourceLocationFromNode(member),
636
+ });
637
+ } else {
638
+ const constantValue = checker.getConstantValue(member);
639
+ if (typeof constantValue === "number") {
640
+ members.push({
641
+ name,
642
+ value: name,
643
+ numericValue: constantValue,
644
+ description: tsdocInfo.description ?? null,
645
+ deprecated: tsdocInfo.deprecated ?? null,
646
+ sourceLocation: getSourceLocationFromNode(member),
647
+ });
648
+ }
649
+ }
650
+ }
651
+
652
+ return members;
653
+ }
654
+
655
+ const GRAPHQL_NAME_REGEX = /^[_A-Za-z][_0-9A-Za-z]*$/;
656
+
657
+ function isValidGraphQLName(name: string): boolean {
658
+ return GRAPHQL_NAME_REGEX.test(name);
659
+ }
660
+
661
+ function validateNumericEnumMembers(
662
+ members: ReadonlyArray<EnumMemberInfo>,
663
+ enumName: string,
664
+ enumLocation: SourceLocation,
665
+ ): Diagnostic[] {
666
+ const diagnostics: Diagnostic[] = [];
667
+
668
+ const numericMembers = members.filter((m) => m.numericValue !== null);
669
+ if (numericMembers.length === 0) {
670
+ return diagnostics;
671
+ }
672
+
673
+ const valueToMembers = new Map<number, string[]>();
674
+ for (const member of numericMembers) {
675
+ const value = member.numericValue!;
676
+ const existing = valueToMembers.get(value) ?? [];
677
+ existing.push(member.name);
678
+ valueToMembers.set(value, existing);
679
+ }
680
+
681
+ for (const [value, memberNames] of valueToMembers) {
682
+ if (memberNames.length > 1) {
683
+ diagnostics.push({
684
+ code: "DUPLICATE_ENUM_VALUE",
685
+ message: `Enum '${enumName}' has duplicate numeric value ${value} (used by ${memberNames.join(" and ")})`,
686
+ severity: "error",
687
+ location: enumLocation,
688
+ });
689
+ }
690
+ }
691
+
692
+ for (const member of members) {
693
+ if (!isValidGraphQLName(member.name)) {
694
+ diagnostics.push({
695
+ code: "INVALID_ENUM_MEMBER_NAME",
696
+ message: `Enum member '${enumName}.${member.name}' is not a valid GraphQL identifier`,
697
+ severity: "error",
698
+ location: member.sourceLocation ?? enumLocation,
699
+ });
700
+ }
701
+ }
702
+
703
+ return diagnostics;
704
+ }
705
+
706
+ function extractStringLiteralUnionMembers(
707
+ type: ts.Type,
708
+ checker: ts.TypeChecker,
709
+ ): ReadonlyArray<EnumMemberInfo> {
710
+ if (!type.isUnion()) return [];
711
+
712
+ const members: EnumMemberInfo[] = [];
713
+
714
+ for (const t of type.types) {
715
+ if (isNullOrUndefined(t)) {
716
+ continue;
717
+ }
718
+ if (t.flags & ts.TypeFlags.StringLiteral) {
719
+ const value = checker.typeToString(t).replace(/^"|"$/g, "");
720
+ members.push({
721
+ name: value,
722
+ value: value,
723
+ numericValue: null,
724
+ description: null,
725
+ deprecated: null,
726
+ sourceLocation: null,
727
+ });
728
+ }
729
+ }
730
+
731
+ return members;
732
+ }
733
+
734
+ function determineTypeKind(
735
+ node: ts.Node,
736
+ type: ts.Type,
737
+ checker: ts.TypeChecker,
738
+ ): TypeKind {
739
+ if (ts.isInterfaceDeclaration(node)) {
740
+ return "interface";
741
+ }
742
+
743
+ if (ts.isTypeAliasDeclaration(node)) {
744
+ if (isDefineInterfaceTypeAlias(node, checker)) {
745
+ return "graphqlInterface";
746
+ }
747
+
748
+ const unionKind = determineTypeKindFromUnion(type);
749
+ if (unionKind) {
750
+ return unionKind;
751
+ }
752
+ return "object";
753
+ }
754
+
755
+ return "object";
756
+ }
757
+
758
+ function determineTypeKindFromUnion(type: ts.Type): TypeKind | null {
759
+ if (!type.isUnion()) {
760
+ return null;
761
+ }
762
+
763
+ const nonNullTypes = getNonNullableTypes(type);
764
+
765
+ if (isStringLiteralUnion(type)) {
766
+ return "enum";
767
+ }
768
+
769
+ const allObjectTypes = nonNullTypes.every(
770
+ (t) =>
771
+ (t.flags & ts.TypeFlags.Object) !== 0 ||
772
+ (t.flags & ts.TypeFlags.Intersection) !== 0 ||
773
+ t.symbol !== undefined,
774
+ );
775
+ if (nonNullTypes.length > 1 && allObjectTypes) {
776
+ return "union";
777
+ }
778
+
779
+ return null;
780
+ }
781
+
782
+ function determineTypeKindFromType(
783
+ type: ts.Type,
784
+ originalSymbol: ts.Symbol,
785
+ ): TypeKind {
786
+ const declarations = originalSymbol.getDeclarations();
787
+ const declaration = declarations?.[0];
788
+
789
+ if (declaration && ts.isInterfaceDeclaration(declaration)) {
790
+ return "interface";
791
+ }
792
+
793
+ if (declaration && ts.isEnumDeclaration(declaration)) {
794
+ return "enum";
795
+ }
796
+
797
+ const unionKind = determineTypeKindFromUnion(type);
798
+ if (unionKind) {
799
+ return unionKind;
800
+ }
801
+
802
+ return "object";
803
+ }
804
+
805
+ function isDeclarationInScannedFiles(
806
+ declaration: ts.Declaration,
807
+ scannedSourceFiles: ReadonlySet<string>,
808
+ ): boolean {
809
+ const declSourceFileName = resolve(declaration.getSourceFile().fileName);
810
+ return Array.from(scannedSourceFiles).some(
811
+ (sf) => resolve(sf) === declSourceFileName,
812
+ );
813
+ }
814
+
815
+ function createGenericTypeDiagnostic(
816
+ declaration: ts.Declaration,
817
+ exportedName: string,
818
+ location: SourceLocation,
819
+ ): Diagnostic | null {
820
+ if (
821
+ (ts.isTypeAliasDeclaration(declaration) ||
822
+ ts.isInterfaceDeclaration(declaration)) &&
823
+ declaration.typeParameters &&
824
+ declaration.typeParameters.length > 0
825
+ ) {
826
+ return {
827
+ code: "UNSUPPORTED_SYNTAX",
828
+ message: `Generic type '${exportedName}' is not supported. Consider using a concrete type instead.`,
829
+ severity: "warning",
830
+ location,
831
+ };
832
+ }
833
+ return null;
834
+ }
835
+
836
+ interface ProcessReexportedSymbolParams {
837
+ readonly exportedName: string;
838
+ readonly resolvedSymbol: ts.Symbol;
839
+ readonly type: ts.Type;
840
+ readonly location: SourceLocation;
841
+ readonly filePath: string;
842
+ readonly checker: ts.TypeChecker;
843
+ readonly globalTypeMappings: ReadonlyArray<GlobalTypeMapping>;
844
+ readonly knownTypeNames: ReadonlySet<string>;
845
+ readonly knownTypeSymbols: ReadonlyMap<string, ts.Symbol>;
846
+ readonly underlyingSymbolToTypeName: ReadonlyMap<ts.Symbol, string>;
847
+ readonly scannedSourceFiles: ReadonlySet<string>;
848
+ readonly scalarMappingTable: ScalarBaseTypeMappingTable | null;
849
+ readonly scalarMappingContext: ScalarMappingContext;
850
+ }
851
+
852
+ interface ProcessReexportedSymbolResult {
853
+ readonly typeInfo: ExtractedTypeInfo | null;
854
+ readonly diagnostics: Diagnostic[];
855
+ readonly scalarName: string | null;
856
+ readonly scalarMetadata: ScalarMetadataInfo | null;
857
+ readonly skip: boolean;
858
+ }
859
+
860
+ function processReexportedSymbol(
861
+ params: ProcessReexportedSymbolParams,
862
+ ): ProcessReexportedSymbolResult {
863
+ const {
864
+ exportedName,
865
+ resolvedSymbol,
866
+ type,
867
+ location,
868
+ filePath,
869
+ checker,
870
+ globalTypeMappings,
871
+ knownTypeNames,
872
+ knownTypeSymbols,
873
+ underlyingSymbolToTypeName,
874
+ scannedSourceFiles,
875
+ scalarMappingTable,
876
+ scalarMappingContext,
877
+ } = params;
878
+
879
+ const diagnostics: Diagnostic[] = [];
880
+
881
+ const scalarMetadataResult = detectScalarMetadata(type, checker);
882
+ if (scalarMetadataResult.scalarName && !scalarMetadataResult.isPrimitive) {
883
+ const tsdocInfo = extractTsDocFromSymbol(resolvedSymbol, checker);
884
+ return {
885
+ typeInfo: null,
886
+ diagnostics: [],
887
+ scalarName: scalarMetadataResult.scalarName,
888
+ scalarMetadata: {
889
+ scalarName: scalarMetadataResult.scalarName,
890
+ typeName: exportedName,
891
+ only: scalarMetadataResult.only,
892
+ sourceFile: filePath,
893
+ line: location.line,
894
+ description: tsdocInfo.description ?? null,
895
+ },
896
+ skip: false,
897
+ };
898
+ }
899
+
900
+ const declarations = resolvedSymbol.getDeclarations();
901
+ const declaration = declarations?.[0];
902
+ if (declaration) {
903
+ if (
904
+ isDeclarationInScannedFiles(declaration, scannedSourceFiles) &&
905
+ (ts.isTypeAliasDeclaration(declaration) ||
906
+ ts.isInterfaceDeclaration(declaration) ||
907
+ ts.isEnumDeclaration(declaration))
908
+ ) {
909
+ return {
910
+ typeInfo: null,
911
+ diagnostics: [],
912
+ scalarName: null,
913
+ scalarMetadata: null,
914
+ skip: true,
915
+ };
916
+ }
917
+
918
+ const genericDiagnostic = createGenericTypeDiagnostic(
919
+ declaration,
920
+ exportedName,
921
+ location,
922
+ );
923
+ if (genericDiagnostic) {
924
+ diagnostics.push(genericDiagnostic);
925
+ }
926
+ }
927
+
928
+ const kind = determineTypeKindFromType(type, resolvedSymbol);
929
+ const tsdocInfo = extractTsDocFromSymbol(resolvedSymbol, checker);
930
+
931
+ const metadata: TypeMetadata = {
932
+ name: exportedName,
933
+ kind,
934
+ sourceFile: filePath,
935
+ sourceLocation: location,
936
+ exportKind: "named",
937
+ description: tsdocInfo.description ?? null,
938
+ deprecated: tsdocInfo.deprecated ?? null,
939
+ directives: null,
940
+ };
941
+
942
+ if (kind === "enum") {
943
+ const declarations = resolvedSymbol.getDeclarations();
944
+ const declaration = declarations?.[0];
945
+ let enumMembers: ReadonlyArray<EnumMemberInfo>;
946
+ if (declaration && ts.isEnumDeclaration(declaration)) {
947
+ enumMembers = extractEnumMembers(declaration, checker);
948
+ } else {
949
+ enumMembers = extractStringLiteralUnionMembers(type, checker);
950
+ }
951
+ return {
952
+ typeInfo: {
953
+ metadata,
954
+ fields: [],
955
+ unionMembers: null,
956
+ inlineObjectMembers: null,
957
+ enumMembers,
958
+ implementedInterfaces: null,
959
+ },
960
+ diagnostics,
961
+ scalarName: null,
962
+ scalarMetadata: null,
963
+ skip: false,
964
+ };
965
+ }
966
+
967
+ // Get typeNode for union member extraction from declaration
968
+ const reexportDeclarations = resolvedSymbol.getDeclarations();
969
+ const reexportDeclaration = reexportDeclarations?.[0];
970
+ const reexportTypeNode =
971
+ reexportDeclaration && ts.isTypeAliasDeclaration(reexportDeclaration)
972
+ ? reexportDeclaration.type
973
+ : undefined;
974
+ const unionMembers = extractUnionMembers(type, reexportTypeNode);
975
+ const ignoreFields = detectIgnoreFieldsMetadata({ type, checker });
976
+
977
+ if (ignoreFields !== null && kind !== "union") {
978
+ const allFieldNames = collectAllFieldNames(type, checker);
979
+ const validationDiagnostics = validateIgnoreFields({
980
+ typeName: exportedName,
981
+ ignoreFields,
982
+ allFieldNames,
983
+ sourceLocation: location,
984
+ });
985
+ diagnostics.push(...validationDiagnostics);
986
+ }
987
+
988
+ const fieldResult =
989
+ kind === "union"
990
+ ? { fields: [], diagnostics: [] }
991
+ : extractFieldsFromType({
992
+ type,
993
+ checker,
994
+ globalTypeMappings,
995
+ knownTypeNames,
996
+ knownTypeSymbols,
997
+ underlyingSymbolToTypeName,
998
+ sourceFiles: scannedSourceFiles,
999
+ scalarMappingTable,
1000
+ scalarMappingContext,
1001
+ ignoreFields,
1002
+ });
1003
+ diagnostics.push(...fieldResult.diagnostics);
1004
+
1005
+ return {
1006
+ typeInfo: {
1007
+ metadata,
1008
+ fields: fieldResult.fields,
1009
+ unionMembers: unionMembers ?? null,
1010
+ inlineObjectMembers: null,
1011
+ enumMembers: null,
1012
+ implementedInterfaces: null,
1013
+ },
1014
+ diagnostics,
1015
+ scalarName: null,
1016
+ scalarMetadata: null,
1017
+ skip: false,
1018
+ };
1019
+ }
1020
+
1021
+ interface ProcessExportDeclarationResult {
1022
+ readonly types: ExtractedTypeInfo[];
1023
+ readonly diagnostics: Diagnostic[];
1024
+ readonly detectedScalarNames: string[];
1025
+ readonly detectedScalars: ScalarMetadataInfo[];
1026
+ }
1027
+
1028
+ function processExportDeclaration(
1029
+ node: ts.ExportDeclaration,
1030
+ sourceFile: ts.SourceFile,
1031
+ filePath: string,
1032
+ checker: ts.TypeChecker,
1033
+ globalTypeMappings: ReadonlyArray<GlobalTypeMapping>,
1034
+ knownTypeNames: ReadonlySet<string>,
1035
+ knownTypeSymbols: ReadonlyMap<string, ts.Symbol>,
1036
+ underlyingSymbolToTypeName: ReadonlyMap<ts.Symbol, string>,
1037
+ scannedSourceFiles: ReadonlySet<string>,
1038
+ scalarMappingTable: ScalarBaseTypeMappingTable | null,
1039
+ ): ProcessExportDeclarationResult {
1040
+ const types: ExtractedTypeInfo[] = [];
1041
+ const diagnostics: Diagnostic[] = [];
1042
+ const detectedScalarNames: string[] = [];
1043
+ const detectedScalars: ScalarMetadataInfo[] = [];
1044
+
1045
+ if (!node.isTypeOnly) {
1046
+ return { types, diagnostics, detectedScalarNames, detectedScalars };
1047
+ }
1048
+
1049
+ const exportClause = node.exportClause;
1050
+
1051
+ const symbolsToProcess: Array<{
1052
+ exportedName: string;
1053
+ resolvedSymbol: ts.Symbol;
1054
+ type: ts.Type;
1055
+ }> = [];
1056
+
1057
+ if (exportClause && ts.isNamedExports(exportClause)) {
1058
+ for (const specifier of exportClause.elements) {
1059
+ const exportedName = specifier.name.text;
1060
+ const localTargetSymbol =
1061
+ checker.getExportSpecifierLocalTargetSymbol(specifier);
1062
+ if (!localTargetSymbol) continue;
1063
+
1064
+ const originalSymbol =
1065
+ localTargetSymbol.flags & ts.SymbolFlags.Alias
1066
+ ? checker.getAliasedSymbol(localTargetSymbol)
1067
+ : localTargetSymbol;
1068
+ if (!originalSymbol) continue;
1069
+
1070
+ const type = checker.getDeclaredTypeOfSymbol(originalSymbol);
1071
+ symbolsToProcess.push({
1072
+ exportedName,
1073
+ resolvedSymbol: originalSymbol,
1074
+ type,
1075
+ });
1076
+ }
1077
+ } else if (!exportClause && node.moduleSpecifier) {
1078
+ const moduleSymbol = checker.getSymbolAtLocation(node.moduleSpecifier);
1079
+ if (!moduleSymbol) {
1080
+ const location = getSourceLocationFromNode(node)!;
1081
+ const modulePath = ts.isStringLiteral(node.moduleSpecifier)
1082
+ ? node.moduleSpecifier.text
1083
+ : node.moduleSpecifier.getText(sourceFile);
1084
+ diagnostics.push({
1085
+ code: "MODULE_RESOLUTION_ERROR",
1086
+ message: `Could not resolve module '${modulePath}'`,
1087
+ severity: "error",
1088
+ location,
1089
+ });
1090
+ return { types, diagnostics, detectedScalarNames, detectedScalars };
1091
+ }
1092
+
1093
+ const exports = checker.getExportsOfModule(moduleSymbol);
1094
+ for (const exportedSymbol of exports) {
1095
+ const resolvedSymbol =
1096
+ exportedSymbol.flags & ts.SymbolFlags.Alias
1097
+ ? checker.getAliasedSymbol(exportedSymbol)
1098
+ : exportedSymbol;
1099
+
1100
+ if (
1101
+ !(
1102
+ resolvedSymbol.flags & ts.SymbolFlags.TypeAlias ||
1103
+ resolvedSymbol.flags & ts.SymbolFlags.Interface ||
1104
+ resolvedSymbol.flags & ts.SymbolFlags.Enum
1105
+ )
1106
+ ) {
1107
+ continue;
1108
+ }
1109
+
1110
+ const type = checker.getDeclaredTypeOfSymbol(resolvedSymbol);
1111
+ symbolsToProcess.push({
1112
+ exportedName: exportedSymbol.getName(),
1113
+ resolvedSymbol,
1114
+ type,
1115
+ });
1116
+ }
1117
+ }
1118
+
1119
+ const location = getSourceLocationFromNode(node)!;
1120
+ for (const { exportedName, resolvedSymbol, type } of symbolsToProcess) {
1121
+ const result = processReexportedSymbol({
1122
+ exportedName,
1123
+ resolvedSymbol,
1124
+ type,
1125
+ location,
1126
+ filePath,
1127
+ checker,
1128
+ globalTypeMappings,
1129
+ knownTypeNames,
1130
+ knownTypeSymbols,
1131
+ underlyingSymbolToTypeName,
1132
+ scannedSourceFiles,
1133
+ scalarMappingTable,
1134
+ scalarMappingContext: exportedName.endsWith("Input") ? "input" : "output",
1135
+ });
1136
+
1137
+ if (result.skip) continue;
1138
+
1139
+ if (result.scalarName && result.scalarMetadata) {
1140
+ detectedScalarNames.push(result.scalarName);
1141
+ detectedScalars.push(result.scalarMetadata);
1142
+ continue;
1143
+ }
1144
+
1145
+ diagnostics.push(...result.diagnostics);
1146
+ if (result.typeInfo) {
1147
+ types.push(result.typeInfo);
1148
+ }
1149
+ }
1150
+
1151
+ return { types, diagnostics, detectedScalarNames, detectedScalars };
1152
+ }
1153
+
1154
+ function getNamedTypeName(memberType: ts.Type): string {
1155
+ // For type aliases (e.g., GqlObject<...>), use aliasSymbol
1156
+ if (memberType.aliasSymbol) {
1157
+ return memberType.aliasSymbol.getName();
1158
+ }
1159
+ // For regular types, use symbol
1160
+ return memberType.symbol?.getName() ?? "";
1161
+ }
1162
+
1163
+ interface InlineObjectExtractionResult {
1164
+ readonly members: InlineObjectMember[];
1165
+ readonly hasInlineObjects: boolean;
1166
+ readonly hasNamedTypes: boolean;
1167
+ }
1168
+
1169
+ interface ExtractInlineObjectMembersParams {
1170
+ readonly type: ts.Type;
1171
+ readonly checker: ts.TypeChecker;
1172
+ readonly globalTypeMappings: ReadonlyArray<GlobalTypeMapping>;
1173
+ readonly knownTypeNames: ReadonlySet<string>;
1174
+ readonly typeNode: ts.TypeNode | undefined;
1175
+ }
1176
+
1177
+ function extractInlineObjectMembers(
1178
+ params: ExtractInlineObjectMembersParams,
1179
+ ): InlineObjectExtractionResult | null {
1180
+ const { type, checker, globalTypeMappings, knownTypeNames, typeNode } =
1181
+ params;
1182
+ if (!type.isUnion()) {
1183
+ return null;
1184
+ }
1185
+
1186
+ const nonNullTypes = getNonNullableTypes(type);
1187
+ const memberTypeNodes =
1188
+ typeNode && ts.isUnionTypeNode(typeNode)
1189
+ ? filterNonNullTypeNodes(typeNode)
1190
+ : [];
1191
+
1192
+ const allObjectTypes = nonNullTypes.every(
1193
+ (t) =>
1194
+ (t.flags & ts.TypeFlags.Object) !== 0 ||
1195
+ (t.flags & ts.TypeFlags.Intersection) !== 0,
1196
+ );
1197
+
1198
+ if (nonNullTypes.length < 2 || !allObjectTypes) {
1199
+ return null;
1200
+ }
1201
+
1202
+ let hasInlineObjects = false;
1203
+ let hasNamedTypes = false;
1204
+ const members: InlineObjectMember[] = [];
1205
+
1206
+ const ctx: TypeDeclarationContext = {
1207
+ checker,
1208
+ globalTypeMappings,
1209
+ knownTypeNames,
1210
+ visitedTypes: new WeakSet(),
1211
+ };
1212
+
1213
+ if (memberTypeNodes.length > 0) {
1214
+ for (const memberNode of memberTypeNodes) {
1215
+ if (ts.isTypeReferenceNode(memberNode)) {
1216
+ hasNamedTypes = true;
1217
+ } else {
1218
+ hasInlineObjects = true;
1219
+ }
1220
+ }
1221
+ } else {
1222
+ for (const memberType of nonNullTypes) {
1223
+ if (isAnonymousObjectType(memberType)) {
1224
+ hasInlineObjects = true;
1225
+ } else {
1226
+ hasNamedTypes = true;
1227
+ }
1228
+ }
1229
+ }
1230
+
1231
+ if (hasInlineObjects) {
1232
+ for (const memberType of nonNullTypes) {
1233
+ if (isAnonymousObjectType(memberType)) {
1234
+ const properties = memberType.getProperties();
1235
+ const memberProperties: InlineObjectProperty[] = [];
1236
+
1237
+ for (const prop of properties) {
1238
+ const propType = checker.getTypeOfSymbol(prop);
1239
+ const tsdocInfo = extractTsDocFromSymbol(prop, checker);
1240
+ const typeResult = convertTsTypeToReference(propType, ctx);
1241
+
1242
+ memberProperties.push({
1243
+ propertyName: prop.getName(),
1244
+ propertyType: typeResult.tsType,
1245
+ description: tsdocInfo.description ?? null,
1246
+ deprecated: tsdocInfo.deprecated ?? null,
1247
+ });
1248
+ }
1249
+
1250
+ members.push({ properties: memberProperties });
1251
+ }
1252
+ }
1253
+ }
1254
+
1255
+ return { members, hasInlineObjects, hasNamedTypes };
1256
+ }
1257
+
1258
+ function extractUnionMembers(
1259
+ type: ts.Type,
1260
+ typeNode?: ts.TypeNode,
1261
+ ): string[] | undefined {
1262
+ if (!type.isUnion()) {
1263
+ return undefined;
1264
+ }
1265
+
1266
+ const nonNullTypes = getNonNullableTypes(type);
1267
+
1268
+ const allObjectTypes = nonNullTypes.every(
1269
+ (t) =>
1270
+ (t.flags & ts.TypeFlags.Object) !== 0 ||
1271
+ (t.flags & ts.TypeFlags.Intersection) !== 0 ||
1272
+ t.symbol !== undefined,
1273
+ );
1274
+
1275
+ if (nonNullTypes.length > 1 && allObjectTypes) {
1276
+ // Extract member type nodes from union type node if available
1277
+ const memberTypeNodes =
1278
+ typeNode && ts.isUnionTypeNode(typeNode)
1279
+ ? filterNonNullTypeNodes(typeNode)
1280
+ : [];
1281
+
1282
+ const namedMembers = nonNullTypes
1283
+ .map((t, index) => {
1284
+ // First try to get name from type
1285
+ if (!isAnonymousObjectType(t)) {
1286
+ const name = getNamedTypeName(t);
1287
+ if (name !== "" && name !== "__type") {
1288
+ return name;
1289
+ }
1290
+ }
1291
+ // Fallback to typeNode name for Simplify<T> pattern
1292
+ if (memberTypeNodes[index]) {
1293
+ const memberNode = memberTypeNodes[index];
1294
+ if (ts.isTypeReferenceNode(memberNode)) {
1295
+ return getTypeNameFromNode(memberNode) ?? "";
1296
+ }
1297
+ }
1298
+ return "";
1299
+ })
1300
+ .filter((name) => name !== "" && name !== "__type");
1301
+
1302
+ if (namedMembers.length > 0) {
1303
+ return namedMembers.sort();
1304
+ }
1305
+ }
1306
+
1307
+ return undefined;
1308
+ }
1309
+
1310
+ export function extractTypesFromProgram(
1311
+ program: ts.Program,
1312
+ sourceFiles: ReadonlyArray<string>,
1313
+ options: ExtractionOptions,
1314
+ ): ExtractionResult {
1315
+ const checker = program.getTypeChecker();
1316
+ const types: ExtractedTypeInfo[] = [];
1317
+ const diagnostics: Diagnostic[] = [];
1318
+ const detectedScalarNames = new Set<string>();
1319
+ const detectedScalars: ScalarMetadataInfo[] = [];
1320
+ const {
1321
+ globalTypeMappings,
1322
+ knownTypeNames,
1323
+ knownTypeSymbols,
1324
+ underlyingSymbolToTypeName,
1325
+ scalarMappingTable,
1326
+ } = options;
1327
+ const scannedSourceFilesSet = new Set(sourceFiles);
1328
+
1329
+ for (const filePath of sourceFiles) {
1330
+ const sourceFile = program.getSourceFile(filePath);
1331
+ if (!sourceFile) {
1332
+ diagnostics.push({
1333
+ code: "PARSE_ERROR",
1334
+ message: `Could not load source file: ${filePath}`,
1335
+ severity: "error",
1336
+ location: { file: filePath, line: 1, column: 1 },
1337
+ });
1338
+ continue;
1339
+ }
1340
+
1341
+ ts.forEachChild(sourceFile, (node) => {
1342
+ if (ts.isEnumDeclaration(node)) {
1343
+ const hasExport = isExported(node);
1344
+ const hasDefaultExport = isDefaultExport(node, sourceFile);
1345
+
1346
+ if (!hasExport && !hasDefaultExport) {
1347
+ return;
1348
+ }
1349
+
1350
+ const name = node.name.getText(sourceFile);
1351
+ const location = getSourceLocationFromNode(node)!;
1352
+
1353
+ if (isConstEnum(node)) {
1354
+ diagnostics.push({
1355
+ code: "UNSUPPORTED_ENUM_TYPE",
1356
+ message: `Const enum '${name}' is not supported. Use a regular enum instead.`,
1357
+ severity: "error",
1358
+ location,
1359
+ });
1360
+ return;
1361
+ }
1362
+
1363
+ if (isHeterogeneousEnum(node)) {
1364
+ diagnostics.push({
1365
+ code: "UNSUPPORTED_ENUM_TYPE",
1366
+ message: `Heterogeneous enum '${name}' is not supported. Use a string enum instead.`,
1367
+ severity: "error",
1368
+ location,
1369
+ });
1370
+ return;
1371
+ }
1372
+
1373
+ const enumMembers = extractEnumMembers(node, checker);
1374
+
1375
+ const validationDiagnostics = validateNumericEnumMembers(
1376
+ enumMembers,
1377
+ name,
1378
+ location,
1379
+ );
1380
+ if (validationDiagnostics.length > 0) {
1381
+ diagnostics.push(...validationDiagnostics);
1382
+ return;
1383
+ }
1384
+
1385
+ const tsdocInfo = extractTsDocInfo(node, checker);
1386
+ const metadata: TypeMetadata = {
1387
+ name,
1388
+ kind: "enum",
1389
+ sourceFile: filePath,
1390
+ sourceLocation: location,
1391
+ exportKind: hasDefaultExport ? "default" : "named",
1392
+ description: tsdocInfo.description,
1393
+ deprecated: tsdocInfo.deprecated,
1394
+ directives: null,
1395
+ };
1396
+
1397
+ types.push({
1398
+ metadata,
1399
+ fields: [],
1400
+ unionMembers: null,
1401
+ inlineObjectMembers: null,
1402
+ enumMembers,
1403
+ implementedInterfaces: null,
1404
+ });
1405
+ return;
1406
+ }
1407
+
1408
+ if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
1409
+ const hasExport = isExported(node);
1410
+ const hasDefaultExport = isDefaultExport(node, sourceFile);
1411
+
1412
+ if (!hasExport && !hasDefaultExport) {
1413
+ return;
1414
+ }
1415
+
1416
+ const name = node.name.getText(sourceFile);
1417
+ const typeSourceLocation = getSourceLocationFromNode(node)!;
1418
+
1419
+ if (node.typeParameters && node.typeParameters.length > 0) {
1420
+ diagnostics.push({
1421
+ code: "UNSUPPORTED_SYNTAX",
1422
+ message: `Generic type '${name}' is not supported. Consider using a concrete type instead.`,
1423
+ severity: "warning",
1424
+ location: typeSourceLocation,
1425
+ });
1426
+ }
1427
+
1428
+ const symbol = checker.getSymbolAtLocation(node.name);
1429
+ if (!symbol) {
1430
+ return;
1431
+ }
1432
+
1433
+ const type = checker.getDeclaredTypeOfSymbol(symbol);
1434
+
1435
+ const scalarMetadata = detectScalarMetadata(type, checker);
1436
+ if (scalarMetadata.scalarName && !scalarMetadata.isPrimitive) {
1437
+ detectedScalarNames.add(scalarMetadata.scalarName);
1438
+ const tsdocInfo = extractTsDocInfo(node, checker);
1439
+ detectedScalars.push({
1440
+ scalarName: scalarMetadata.scalarName,
1441
+ typeName: name,
1442
+ only: scalarMetadata.only,
1443
+ sourceFile: filePath,
1444
+ line: typeSourceLocation.line,
1445
+ description: tsdocInfo.description ?? null,
1446
+ });
1447
+ return;
1448
+ }
1449
+
1450
+ let typeDirectives: ReadonlyArray<DirectiveInfo> | null = null;
1451
+ let actualType = type;
1452
+
1453
+ if (hasDirectiveMetadata(type)) {
1454
+ const directiveResult = detectDirectiveMetadata(type, checker);
1455
+ if (directiveResult.directives.length > 0) {
1456
+ typeDirectives = directiveResult.directives;
1457
+ }
1458
+ if (directiveResult.errors.length > 0) {
1459
+ for (const error of directiveResult.errors) {
1460
+ diagnostics.push({
1461
+ code: error.code,
1462
+ message: `Type '${name}': ${error.message}`,
1463
+ severity: "error",
1464
+ location: typeSourceLocation,
1465
+ });
1466
+ }
1467
+ }
1468
+ actualType = type;
1469
+ }
1470
+
1471
+ const kind = determineTypeKind(node, actualType, checker);
1472
+ // Get typeNode for union member extraction (only for type aliases)
1473
+ const typeAliasTypeNode = ts.isTypeAliasDeclaration(node)
1474
+ ? node.type
1475
+ : undefined;
1476
+ const unionMembers = extractUnionMembers(actualType, typeAliasTypeNode);
1477
+ const inlineObjectResult = extractInlineObjectMembers({
1478
+ type: actualType,
1479
+ checker,
1480
+ globalTypeMappings,
1481
+ knownTypeNames,
1482
+ typeNode: typeAliasTypeNode,
1483
+ });
1484
+ const tsdocInfo = extractTsDocInfo(node, checker);
1485
+
1486
+ let implementedInterfaces: ReadonlyArray<string> | null = null;
1487
+ if (ts.isTypeAliasDeclaration(node)) {
1488
+ if (kind === "graphqlInterface") {
1489
+ const interfaces = extractImplementsFromDefineInterface(
1490
+ node,
1491
+ sourceFile,
1492
+ checker,
1493
+ );
1494
+ if (interfaces.length > 0) {
1495
+ implementedInterfaces = interfaces;
1496
+ }
1497
+ } else {
1498
+ const interfaces = extractImplementsFromGqlTypeDef(
1499
+ node,
1500
+ sourceFile,
1501
+ checker,
1502
+ );
1503
+ if (interfaces.length > 0) {
1504
+ implementedInterfaces = interfaces;
1505
+ }
1506
+ }
1507
+ }
1508
+
1509
+ const metadata: TypeMetadata = {
1510
+ name,
1511
+ kind,
1512
+ sourceFile: filePath,
1513
+ sourceLocation: typeSourceLocation,
1514
+ exportKind: hasDefaultExport ? "default" : "named",
1515
+ description: tsdocInfo.description,
1516
+ deprecated: tsdocInfo.deprecated,
1517
+ directives: typeDirectives,
1518
+ };
1519
+
1520
+ if (kind === "enum") {
1521
+ const enumMembers = extractStringLiteralUnionMembers(
1522
+ actualType,
1523
+ checker,
1524
+ );
1525
+ types.push({
1526
+ metadata,
1527
+ fields: [],
1528
+ unionMembers: null,
1529
+ inlineObjectMembers: null,
1530
+ enumMembers,
1531
+ implementedInterfaces: null,
1532
+ });
1533
+ return;
1534
+ }
1535
+
1536
+ const ignoreFields = detectIgnoreFieldsMetadata({ type, checker });
1537
+
1538
+ if (ignoreFields !== null && kind !== "union") {
1539
+ const allFieldNames = collectAllFieldNames(type, checker);
1540
+ const validationDiagnostics = validateIgnoreFields({
1541
+ typeName: name,
1542
+ ignoreFields,
1543
+ allFieldNames,
1544
+ sourceLocation: typeSourceLocation,
1545
+ });
1546
+ diagnostics.push(...validationDiagnostics);
1547
+ }
1548
+
1549
+ const fieldResult =
1550
+ kind === "union"
1551
+ ? { fields: [], diagnostics: [] }
1552
+ : extractFieldsFromType({
1553
+ type: actualType,
1554
+ checker,
1555
+ globalTypeMappings,
1556
+ knownTypeNames,
1557
+ knownTypeSymbols,
1558
+ underlyingSymbolToTypeName,
1559
+ sourceFiles: scannedSourceFilesSet,
1560
+ scalarMappingTable,
1561
+ scalarMappingContext: name.endsWith("Input")
1562
+ ? "input"
1563
+ : "output",
1564
+ ignoreFields,
1565
+ });
1566
+ const fields = fieldResult.fields;
1567
+ diagnostics.push(...fieldResult.diagnostics);
1568
+
1569
+ if (name.endsWith("Input") && kind === "union") {
1570
+ if (
1571
+ inlineObjectResult?.hasInlineObjects &&
1572
+ inlineObjectResult.hasNamedTypes
1573
+ ) {
1574
+ diagnostics.push({
1575
+ code: "ONEOF_MIXED_MEMBERS",
1576
+ message: `Input union type '${name}' mixes inline object literals with named type references. Use only inline object literals for oneOf input types.`,
1577
+ severity: "error",
1578
+ location: {
1579
+ ...typeSourceLocation,
1580
+ column: 1,
1581
+ },
1582
+ });
1583
+ } else if (
1584
+ inlineObjectResult?.hasNamedTypes &&
1585
+ !inlineObjectResult.hasInlineObjects
1586
+ ) {
1587
+ diagnostics.push({
1588
+ code: "ONEOF_NAMED_TYPE_UNION",
1589
+ message: `Input union type '${name}' uses named type references instead of inline object literals. Use inline object pattern: type ${name} = { field1: Type1 } | { field2: Type2 }`,
1590
+ severity: "error",
1591
+ location: {
1592
+ ...typeSourceLocation,
1593
+ column: 1,
1594
+ },
1595
+ });
1596
+ }
1597
+ }
1598
+
1599
+ const inlineObjectMembers = inlineObjectResult?.hasInlineObjects
1600
+ ? inlineObjectResult.members
1601
+ : null;
1602
+
1603
+ const typeInfo: ExtractedTypeInfo = {
1604
+ metadata,
1605
+ fields,
1606
+ unionMembers: unionMembers ?? null,
1607
+ inlineObjectMembers,
1608
+ enumMembers: null,
1609
+ implementedInterfaces,
1610
+ };
1611
+
1612
+ types.push(typeInfo);
1613
+ }
1614
+
1615
+ if (ts.isExportDeclaration(node)) {
1616
+ const result = processExportDeclaration(
1617
+ node,
1618
+ sourceFile,
1619
+ filePath,
1620
+ checker,
1621
+ globalTypeMappings,
1622
+ knownTypeNames,
1623
+ knownTypeSymbols,
1624
+ underlyingSymbolToTypeName,
1625
+ scannedSourceFilesSet,
1626
+ scalarMappingTable,
1627
+ );
1628
+ types.push(...result.types);
1629
+ diagnostics.push(...result.diagnostics);
1630
+ for (const scalarName of result.detectedScalarNames) {
1631
+ detectedScalarNames.add(scalarName);
1632
+ }
1633
+ detectedScalars.push(...result.detectedScalars);
1634
+ }
1635
+ });
1636
+ }
1637
+
1638
+ return {
1639
+ types,
1640
+ diagnostics,
1641
+ detectedScalarNames: [...detectedScalarNames],
1642
+ detectedScalars,
1643
+ };
1644
+ }