moonflower 0.9.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 (268) hide show
  1. package/.eslintrc.js +26 -0
  2. package/.prettierrc.js +7 -0
  3. package/README.md +383 -0
  4. package/cli/cli.ts +59 -0
  5. package/cli/entry.cjs +3 -0
  6. package/cli/prettyprint.ts +16 -0
  7. package/dist/cli/cli.d.ts +2 -0
  8. package/dist/cli/cli.d.ts.map +1 -0
  9. package/dist/cli/cli.js +79 -0
  10. package/dist/cli/prettyprint.d.ts +4 -0
  11. package/dist/cli/prettyprint.d.ts.map +1 -0
  12. package/dist/cli/prettyprint.js +18 -0
  13. package/dist/src/errors/BaseHttpError.d.ts +13 -0
  14. package/dist/src/errors/BaseHttpError.d.ts.map +1 -0
  15. package/dist/src/errors/BaseHttpError.js +13 -0
  16. package/dist/src/errors/HttpErrorHandler.d.ts +3 -0
  17. package/dist/src/errors/HttpErrorHandler.d.ts.map +1 -0
  18. package/dist/src/errors/HttpErrorHandler.js +23 -0
  19. package/dist/src/errors/UserFacingErrors.d.ts +11 -0
  20. package/dist/src/errors/UserFacingErrors.d.ts.map +1 -0
  21. package/dist/src/errors/UserFacingErrors.js +23 -0
  22. package/dist/src/hooks/authentication/useAuth.d.ts +3 -0
  23. package/dist/src/hooks/authentication/useAuth.d.ts.map +1 -0
  24. package/dist/src/hooks/authentication/useAuth.js +7 -0
  25. package/dist/src/hooks/authentication/useOptionalAuth.d.ts +3 -0
  26. package/dist/src/hooks/authentication/useOptionalAuth.d.ts.map +1 -0
  27. package/dist/src/hooks/authentication/useOptionalAuth.js +16 -0
  28. package/dist/src/hooks/useApiEndpoint.d.ts +8 -0
  29. package/dist/src/hooks/useApiEndpoint.d.ts.map +1 -0
  30. package/dist/src/hooks/useApiEndpoint.js +7 -0
  31. package/dist/src/hooks/useApiHeader/index.d.ts +2 -0
  32. package/dist/src/hooks/useApiHeader/index.d.ts.map +1 -0
  33. package/dist/src/hooks/useApiHeader/index.js +17 -0
  34. package/dist/src/hooks/useApiHeader/useApiHeader.d.ts +3 -0
  35. package/dist/src/hooks/useApiHeader/useApiHeader.d.ts.map +1 -0
  36. package/dist/src/hooks/useApiHeader/useApiHeader.js +6 -0
  37. package/dist/src/hooks/useApiHeader/useApiHeader.spec.data.d.ts +2 -0
  38. package/dist/src/hooks/useApiHeader/useApiHeader.spec.data.d.ts.map +1 -0
  39. package/dist/src/hooks/useApiHeader/useApiHeader.spec.data.js +22 -0
  40. package/dist/src/hooks/useCookieParams.d.ts +9 -0
  41. package/dist/src/hooks/useCookieParams.d.ts.map +1 -0
  42. package/dist/src/hooks/useCookieParams.js +50 -0
  43. package/dist/src/hooks/useExposeApiModel/index.d.ts +2 -0
  44. package/dist/src/hooks/useExposeApiModel/index.d.ts.map +1 -0
  45. package/dist/src/hooks/useExposeApiModel/index.js +17 -0
  46. package/dist/src/hooks/useExposeApiModel/useExposeApiModel.d.ts +3 -0
  47. package/dist/src/hooks/useExposeApiModel/useExposeApiModel.d.ts.map +1 -0
  48. package/dist/src/hooks/useExposeApiModel/useExposeApiModel.js +9 -0
  49. package/dist/src/hooks/useExposeApiModel/useExposeApiModel.spec.data.d.ts +2 -0
  50. package/dist/src/hooks/useExposeApiModel/useExposeApiModel.spec.data.d.ts.map +1 -0
  51. package/dist/src/hooks/useExposeApiModel/useExposeApiModel.spec.data.js +16 -0
  52. package/dist/src/hooks/useHeaderParams.d.ts +12 -0
  53. package/dist/src/hooks/useHeaderParams.d.ts.map +1 -0
  54. package/dist/src/hooks/useHeaderParams.js +52 -0
  55. package/dist/src/hooks/usePathParams.d.ts +22 -0
  56. package/dist/src/hooks/usePathParams.d.ts.map +1 -0
  57. package/dist/src/hooks/usePathParams.js +46 -0
  58. package/dist/src/hooks/useQueryParams.d.ts +9 -0
  59. package/dist/src/hooks/useQueryParams.d.ts.map +1 -0
  60. package/dist/src/hooks/useQueryParams.js +50 -0
  61. package/dist/src/hooks/useRequestBody.d.ts +9 -0
  62. package/dist/src/hooks/useRequestBody.d.ts.map +1 -0
  63. package/dist/src/hooks/useRequestBody.js +59 -0
  64. package/dist/src/hooks/useRequestRawBody.d.ts +7 -0
  65. package/dist/src/hooks/useRequestRawBody.d.ts.map +1 -0
  66. package/dist/src/hooks/useRequestRawBody.js +34 -0
  67. package/dist/src/index.d.ts +18 -0
  68. package/dist/src/index.d.ts.map +1 -0
  69. package/dist/src/index.js +33 -0
  70. package/dist/src/openapi/analyzerModule/analyzerModule.d.ts +18 -0
  71. package/dist/src/openapi/analyzerModule/analyzerModule.d.ts.map +1 -0
  72. package/dist/src/openapi/analyzerModule/analyzerModule.js +192 -0
  73. package/dist/src/openapi/analyzerModule/nodeParsers.d.ts +19 -0
  74. package/dist/src/openapi/analyzerModule/nodeParsers.d.ts.map +1 -0
  75. package/dist/src/openapi/analyzerModule/nodeParsers.js +521 -0
  76. package/dist/src/openapi/analyzerModule/parseEndpoint.d.ts +4 -0
  77. package/dist/src/openapi/analyzerModule/parseEndpoint.d.ts.map +1 -0
  78. package/dist/src/openapi/analyzerModule/parseEndpoint.js +246 -0
  79. package/dist/src/openapi/analyzerModule/parseExposedModels.d.ts +5 -0
  80. package/dist/src/openapi/analyzerModule/parseExposedModels.d.ts.map +1 -0
  81. package/dist/src/openapi/analyzerModule/parseExposedModels.js +32 -0
  82. package/dist/src/openapi/analyzerModule/test/openApiAnalyzer.spec.data.d.ts +2 -0
  83. package/dist/src/openapi/analyzerModule/test/openApiAnalyzer.spec.data.d.ts.map +1 -0
  84. package/dist/src/openapi/analyzerModule/test/openApiAnalyzer.spec.data.js +400 -0
  85. package/dist/src/openapi/analyzerModule/types.d.ts +53 -0
  86. package/dist/src/openapi/analyzerModule/types.d.ts.map +1 -0
  87. package/dist/src/openapi/analyzerModule/types.js +2 -0
  88. package/dist/src/openapi/discoveryModule/discoverImports/discoverImports.d.ts +8 -0
  89. package/dist/src/openapi/discoveryModule/discoverImports/discoverImports.d.ts.map +1 -0
  90. package/dist/src/openapi/discoveryModule/discoverImports/discoverImports.js +33 -0
  91. package/dist/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterA.spec.data.d.ts +4 -0
  92. package/dist/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterA.spec.data.d.ts.map +1 -0
  93. package/dist/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterA.spec.data.js +8 -0
  94. package/dist/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterB.spec.data.d.ts +4 -0
  95. package/dist/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterB.spec.data.d.ts.map +1 -0
  96. package/dist/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterB.spec.data.js +8 -0
  97. package/dist/src/openapi/discoveryModule/discoverRouterFiles/discoverRouterFiles.d.ts +17 -0
  98. package/dist/src/openapi/discoveryModule/discoverRouterFiles/discoverRouterFiles.d.ts.map +1 -0
  99. package/dist/src/openapi/discoveryModule/discoverRouterFiles/discoverRouterFiles.js +80 -0
  100. package/dist/src/openapi/discoveryModule/discoverRouters/discoverRouters.d.ts +6 -0
  101. package/dist/src/openapi/discoveryModule/discoverRouters/discoverRouters.d.ts.map +1 -0
  102. package/dist/src/openapi/discoveryModule/discoverRouters/discoverRouters.js +31 -0
  103. package/dist/src/openapi/discoveryModule/discoverRouters/discoverRouters.spec.data.d.ts +5 -0
  104. package/dist/src/openapi/discoveryModule/discoverRouters/discoverRouters.spec.data.d.ts.map +1 -0
  105. package/dist/src/openapi/discoveryModule/discoverRouters/discoverRouters.spec.data.js +38 -0
  106. package/dist/src/openapi/discoveryModule/index.d.ts +3 -0
  107. package/dist/src/openapi/discoveryModule/index.d.ts.map +1 -0
  108. package/dist/src/openapi/discoveryModule/index.js +18 -0
  109. package/dist/src/openapi/generatorModule/generateComponentSchemas.d.ts +4 -0
  110. package/dist/src/openapi/generatorModule/generateComponentSchemas.d.ts.map +1 -0
  111. package/dist/src/openapi/generatorModule/generateComponentSchemas.js +12 -0
  112. package/dist/src/openapi/generatorModule/generatePaths.d.ts +10 -0
  113. package/dist/src/openapi/generatorModule/generatePaths.d.ts.map +1 -0
  114. package/dist/src/openapi/generatorModule/generatePaths.js +116 -0
  115. package/dist/src/openapi/generatorModule/generatorModule.d.ts +16 -0
  116. package/dist/src/openapi/generatorModule/generatorModule.d.ts.map +1 -0
  117. package/dist/src/openapi/generatorModule/generatorModule.js +18 -0
  118. package/dist/src/openapi/generatorModule/getSchema.d.ts +36 -0
  119. package/dist/src/openapi/generatorModule/getSchema.d.ts.map +1 -0
  120. package/dist/src/openapi/generatorModule/getSchema.js +133 -0
  121. package/dist/src/openapi/generatorModule/index.d.ts +5 -0
  122. package/dist/src/openapi/generatorModule/index.d.ts.map +1 -0
  123. package/dist/src/openapi/generatorModule/index.js +20 -0
  124. package/dist/src/openapi/generatorModule/test/openApiGenerator.spec.data.d.ts +515 -0
  125. package/dist/src/openapi/generatorModule/test/openApiGenerator.spec.data.d.ts.map +1 -0
  126. package/dist/src/openapi/generatorModule/test/openApiGenerator.spec.data.js +1119 -0
  127. package/dist/src/openapi/initOpenApiEngine.d.ts +4 -0
  128. package/dist/src/openapi/initOpenApiEngine.d.ts.map +1 -0
  129. package/dist/src/openapi/initOpenApiEngine.js +14 -0
  130. package/dist/src/openapi/manager/OpenApiManager.d.ts +67 -0
  131. package/dist/src/openapi/manager/OpenApiManager.d.ts.map +1 -0
  132. package/dist/src/openapi/manager/OpenApiManager.js +86 -0
  133. package/dist/src/openapi/router/OpenApiRouter.d.ts +4 -0
  134. package/dist/src/openapi/router/OpenApiRouter.d.ts.map +1 -0
  135. package/dist/src/openapi/router/OpenApiRouter.js +11 -0
  136. package/dist/src/openapi/types.d.ts +81 -0
  137. package/dist/src/openapi/types.d.ts.map +1 -0
  138. package/dist/src/openapi/types.js +2 -0
  139. package/dist/src/router/Router.d.ts +23 -0
  140. package/dist/src/router/Router.d.ts.map +1 -0
  141. package/dist/src/router/Router.js +81 -0
  142. package/dist/src/router/responseValueToJson.d.ts +2 -0
  143. package/dist/src/router/responseValueToJson.d.ts.map +1 -0
  144. package/dist/src/router/responseValueToJson.js +10 -0
  145. package/dist/src/setupTests.d.ts +1 -0
  146. package/dist/src/setupTests.d.ts.map +1 -0
  147. package/dist/src/setupTests.js +3 -0
  148. package/dist/src/test/TestAppRouter.d.ts +8 -0
  149. package/dist/src/test/TestAppRouter.d.ts.map +1 -0
  150. package/dist/src/test/TestAppRouter.js +58 -0
  151. package/dist/src/test/app.d.ts +3 -0
  152. package/dist/src/test/app.d.ts.map +1 -0
  153. package/dist/src/test/app.js +41 -0
  154. package/dist/src/utils/TypeUtils.d.ts +22 -0
  155. package/dist/src/utils/TypeUtils.d.ts.map +1 -0
  156. package/dist/src/utils/TypeUtils.js +2 -0
  157. package/dist/src/utils/fromZodSchema.d.ts +2 -0
  158. package/dist/src/utils/fromZodSchema.d.ts.map +1 -0
  159. package/dist/src/utils/fromZodSchema.js +6 -0
  160. package/dist/src/utils/loadTestData.d.ts +2 -0
  161. package/dist/src/utils/loadTestData.d.ts.map +1 -0
  162. package/dist/src/utils/loadTestData.js +39 -0
  163. package/dist/src/utils/mockContext.d.ts +20 -0
  164. package/dist/src/utils/mockContext.d.ts.map +1 -0
  165. package/dist/src/utils/mockContext.js +85 -0
  166. package/dist/src/utils/nameOf.d.ts +5 -0
  167. package/dist/src/utils/nameOf.d.ts.map +1 -0
  168. package/dist/src/utils/nameOf.js +7 -0
  169. package/dist/src/utils/object.d.ts +7 -0
  170. package/dist/src/utils/object.d.ts.map +1 -0
  171. package/dist/src/utils/object.js +21 -0
  172. package/dist/src/utils/printers.d.ts +6 -0
  173. package/dist/src/utils/printers.d.ts.map +1 -0
  174. package/dist/src/utils/printers.js +76 -0
  175. package/dist/src/utils/validationMessages.d.ts +18 -0
  176. package/dist/src/utils/validationMessages.d.ts.map +1 -0
  177. package/dist/src/utils/validationMessages.js +43 -0
  178. package/dist/src/validators/BuiltInValidators.d.ts +61 -0
  179. package/dist/src/validators/BuiltInValidators.d.ts.map +1 -0
  180. package/dist/src/validators/BuiltInValidators.js +66 -0
  181. package/dist/src/validators/InternalParamWrappers.d.ts +5 -0
  182. package/dist/src/validators/InternalParamWrappers.d.ts.map +1 -0
  183. package/dist/src/validators/InternalParamWrappers.js +5 -0
  184. package/dist/src/validators/ParamWrappers.d.ts +11 -0
  185. package/dist/src/validators/ParamWrappers.d.ts.map +1 -0
  186. package/dist/src/validators/ParamWrappers.js +9 -0
  187. package/dist/src/validators/types.d.ts +18 -0
  188. package/dist/src/validators/types.d.ts.map +1 -0
  189. package/dist/src/validators/types.js +2 -0
  190. package/dist/tsconfig.build.tsbuildinfo +1 -0
  191. package/package.json +59 -0
  192. package/src/errors/BaseHttpError.ts +16 -0
  193. package/src/errors/HttpErrorHandler.ts +20 -0
  194. package/src/errors/UserFacingErrors.ts +39 -0
  195. package/src/hooks/authentication/useAuth.ts +8 -0
  196. package/src/hooks/authentication/useOptionalAuth.ts +17 -0
  197. package/src/hooks/useApiEndpoint.spec.ts +11 -0
  198. package/src/hooks/useApiEndpoint.ts +10 -0
  199. package/src/hooks/useApiHeader/index.ts +1 -0
  200. package/src/hooks/useApiHeader/useApiHeader.spec.data.ts +22 -0
  201. package/src/hooks/useApiHeader/useApiHeader.spec.ts +34 -0
  202. package/src/hooks/useApiHeader/useApiHeader.ts +6 -0
  203. package/src/hooks/useCookieParams.spec.ts +174 -0
  204. package/src/hooks/useCookieParams.ts +73 -0
  205. package/src/hooks/useExposeApiModel/index.ts +1 -0
  206. package/src/hooks/useExposeApiModel/useExposeApiModel.spec.data.ts +48 -0
  207. package/src/hooks/useExposeApiModel/useExposeApiModel.spec.ts +388 -0
  208. package/src/hooks/useExposeApiModel/useExposeApiModel.ts +9 -0
  209. package/src/hooks/useHeaderParams.spec.ts +186 -0
  210. package/src/hooks/useHeaderParams.ts +83 -0
  211. package/src/hooks/usePathParams.spec.ts +161 -0
  212. package/src/hooks/usePathParams.ts +89 -0
  213. package/src/hooks/useQueryParams.spec.ts +224 -0
  214. package/src/hooks/useQueryParams.ts +73 -0
  215. package/src/hooks/useRequestBody.spec.ts +215 -0
  216. package/src/hooks/useRequestBody.ts +94 -0
  217. package/src/hooks/useRequestRawBody.spec.ts +154 -0
  218. package/src/hooks/useRequestRawBody.ts +56 -0
  219. package/src/index.ts +17 -0
  220. package/src/openapi/analyzerModule/analyzerModule.ts +228 -0
  221. package/src/openapi/analyzerModule/nodeParsers.ts +648 -0
  222. package/src/openapi/analyzerModule/parseEndpoint.ts +305 -0
  223. package/src/openapi/analyzerModule/parseExposedModels.ts +34 -0
  224. package/src/openapi/analyzerModule/test/openApiAnalyzer.spec.data.ts +521 -0
  225. package/src/openapi/analyzerModule/test/openApiAnalyzer.spec.ts +1043 -0
  226. package/src/openapi/analyzerModule/types.ts +72 -0
  227. package/src/openapi/discoveryModule/discoverImports/discoverImports.ts +43 -0
  228. package/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterA.spec.data.ts +7 -0
  229. package/src/openapi/discoveryModule/discoverRouterFiles/data/testRouterB.spec.data.ts +7 -0
  230. package/src/openapi/discoveryModule/discoverRouterFiles/discoverRouterFiles.spec.ts +36 -0
  231. package/src/openapi/discoveryModule/discoverRouterFiles/discoverRouterFiles.ts +80 -0
  232. package/src/openapi/discoveryModule/discoverRouters/discoverRouters.spec.data.ts +42 -0
  233. package/src/openapi/discoveryModule/discoverRouters/discoverRouters.spec.ts +18 -0
  234. package/src/openapi/discoveryModule/discoverRouters/discoverRouters.ts +39 -0
  235. package/src/openapi/discoveryModule/index.ts +2 -0
  236. package/src/openapi/generatorModule/generateComponentSchemas.ts +12 -0
  237. package/src/openapi/generatorModule/generatePaths.ts +138 -0
  238. package/src/openapi/generatorModule/generatorModule.ts +17 -0
  239. package/src/openapi/generatorModule/getSchema.ts +169 -0
  240. package/src/openapi/generatorModule/index.ts +4 -0
  241. package/src/openapi/generatorModule/test/openApiGenerator.spec.data.ts +1119 -0
  242. package/src/openapi/generatorModule/test/openApiGenerator.spec.ts +783 -0
  243. package/src/openapi/initOpenApiEngine.ts +20 -0
  244. package/src/openapi/manager/OpenApiManager.ts +153 -0
  245. package/src/openapi/router/OpenApiRouter.ts +11 -0
  246. package/src/openapi/types.ts +86 -0
  247. package/src/router/Router.ts +123 -0
  248. package/src/router/responseValueToJson.ts +6 -0
  249. package/src/setupTests.ts +3 -0
  250. package/src/test/TestAppRouter.ts +76 -0
  251. package/src/test/app.spec.ts +130 -0
  252. package/src/test/app.ts +43 -0
  253. package/src/utils/TypeUtils.ts +51 -0
  254. package/src/utils/loadTestData.ts +15 -0
  255. package/src/utils/mockContext.ts +86 -0
  256. package/src/utils/nameOf.ts +7 -0
  257. package/src/utils/object.spec.ts +27 -0
  258. package/src/utils/object.ts +17 -0
  259. package/src/utils/printers.spec.ts +103 -0
  260. package/src/utils/printers.ts +49 -0
  261. package/src/utils/validationMessages.ts +65 -0
  262. package/src/validators/BuiltInValidators.ts +64 -0
  263. package/src/validators/InternalParamWrappers.ts +14 -0
  264. package/src/validators/ParamWrappers.ts +22 -0
  265. package/src/validators/types.ts +35 -0
  266. package/tsconfig.build.json +15 -0
  267. package/tsconfig.json +29 -0
  268. package/vite.config.ts +16 -0
@@ -0,0 +1,72 @@
1
+ export type ShapeOfType =
2
+ | ShapeOfProperty
3
+ | ShapeOfStringLiteral
4
+ | ShapeOfNumberLiteral
5
+ | ShapeOfUnion
6
+ | ShapeOfUnionEntry
7
+ | ShapeOfRecord
8
+ | ShapeOfArray
9
+ | ShapeOfRef
10
+ | ShapeOfTuple
11
+ | ShapeOfTupleEntry
12
+
13
+ export type ShapeOfProperty = {
14
+ role: 'property'
15
+ identifier: string
16
+ shape: string | ShapeOfType[]
17
+ optional: boolean
18
+ }
19
+
20
+ export type ShapeOfStringLiteral = {
21
+ role: 'literal_string'
22
+ shape: string
23
+ optional: boolean
24
+ }
25
+
26
+ export type ShapeOfNumberLiteral = {
27
+ role: 'literal_number'
28
+ shape: string
29
+ optional: boolean
30
+ }
31
+
32
+ export type ShapeOfUnion = {
33
+ role: 'union'
34
+ shape: ShapeOfUnionEntry[]
35
+ optional: boolean
36
+ }
37
+
38
+ export type ShapeOfUnionEntry = {
39
+ role: 'union_entry'
40
+ shape: string | ShapeOfType[]
41
+ optional: boolean
42
+ }
43
+
44
+ export type ShapeOfRecord = {
45
+ role: 'record'
46
+ shape: string | ShapeOfType[]
47
+ optional: boolean
48
+ }
49
+
50
+ export type ShapeOfArray = {
51
+ role: 'array'
52
+ shape: string | ShapeOfType[]
53
+ optional: boolean
54
+ }
55
+
56
+ export type ShapeOfRef = {
57
+ role: 'ref'
58
+ shape: string
59
+ optional: boolean
60
+ }
61
+
62
+ export type ShapeOfTuple = {
63
+ role: 'tuple'
64
+ shape: ShapeOfTupleEntry[]
65
+ optional: boolean
66
+ }
67
+
68
+ export type ShapeOfTupleEntry = {
69
+ role: 'tuple_entry'
70
+ shape: string | ShapeOfType[]
71
+ optional: boolean
72
+ }
@@ -0,0 +1,43 @@
1
+ import { SourceFile, SyntaxKind } from 'ts-morph'
2
+
3
+ type Props = {
4
+ sourceFile: SourceFile
5
+ originalName: string
6
+ }
7
+
8
+ export const discoverImportedName = ({ sourceFile, originalName }: Props): string | null => {
9
+ const importDeclarations = sourceFile.getDescendantsOfKind(SyntaxKind.ImportDeclaration)
10
+ const discoveredName = importDeclarations
11
+ .filter((declaration) => {
12
+ // declaration
13
+ const importPathNode = declaration.getLastChildByKind(SyntaxKind.StringLiteral)
14
+ if (!importPathNode) {
15
+ return false
16
+ }
17
+
18
+ const importPath = importPathNode.getText()
19
+
20
+ return (
21
+ /moonflower/.test(importPath) ||
22
+ process.env.NODE_ENV === 'test' ||
23
+ process.env.NODE_ENV === 'development'
24
+ )
25
+ })
26
+ .map((declaration) => {
27
+ const routerImport = declaration
28
+ .getDescendantsOfKind(SyntaxKind.ImportSpecifier)
29
+ .filter((i) => i.getFirstChildByKind(SyntaxKind.Identifier)?.getText() === originalName)[0]
30
+
31
+ if (!routerImport) {
32
+ return null
33
+ }
34
+
35
+ return routerImport.getLastChildByKindOrThrow(SyntaxKind.Identifier).getText()
36
+ })
37
+ .filter((declaration): declaration is NonNullable<typeof declaration> => declaration !== null)[0]
38
+
39
+ if (!discoveredName) {
40
+ return null
41
+ }
42
+ return discoveredName
43
+ }
@@ -0,0 +1,7 @@
1
+ import { Router } from '../../../../router/Router'
2
+
3
+ export const router = new Router()
4
+
5
+ router.get('/api-a', () => {
6
+ return 'test-data'
7
+ })
@@ -0,0 +1,7 @@
1
+ import { Router } from '../../../../router/Router'
2
+
3
+ export const router = new Router()
4
+
5
+ router.get('/api-b', () => {
6
+ return 'test-data'
7
+ })
@@ -0,0 +1,36 @@
1
+ import { resolve } from 'path'
2
+
3
+ import { discoverRouterFiles } from './discoverRouterFiles'
4
+
5
+ describe('discoverRouterFiles', () => {
6
+ it('discovers routers from folder correctly', () => {
7
+ const { discoveredRouterFiles } = discoverRouterFiles({
8
+ targetPath: resolve(__dirname, '.'),
9
+ tsConfigPath: resolve('./tsconfig.json'),
10
+ })
11
+
12
+ expect(discoveredRouterFiles.length).toEqual(2)
13
+ })
14
+
15
+ it('respects ignoring files by string', () => {
16
+ const { discoveredRouterFiles } = discoverRouterFiles({
17
+ targetPath: resolve(__dirname, '.'),
18
+ tsConfigPath: resolve('./tsconfig.json'),
19
+ excludedFiles: ['testRouterA'],
20
+ })
21
+
22
+ expect(discoveredRouterFiles.length).toEqual(1)
23
+ expect(discoveredRouterFiles[0].fileName.endsWith('testRouterB.spec.data.ts')).toBeTruthy()
24
+ })
25
+
26
+ it('respects ignoring files by regex', () => {
27
+ const { discoveredRouterFiles } = discoverRouterFiles({
28
+ targetPath: resolve(__dirname, '.'),
29
+ tsConfigPath: resolve('./tsconfig.json'),
30
+ excludedFiles: [/B/g],
31
+ })
32
+
33
+ expect(discoveredRouterFiles.length).toEqual(1)
34
+ expect(discoveredRouterFiles[0].fileName.endsWith('testRouterA.spec.data.ts')).toBeTruthy()
35
+ })
36
+ })
@@ -0,0 +1,80 @@
1
+ import * as fs from 'fs'
2
+ import * as path from 'path'
3
+ import { Project } from 'ts-morph'
4
+
5
+ import { discoverRouters } from '../discoverRouters/discoverRouters'
6
+
7
+ export type DiscoveredSourceFile = ReturnType<typeof discoverRouterFiles>['discoveredRouterFiles'][number]
8
+
9
+ export const discoverRouterFiles = ({
10
+ targetPath,
11
+ tsConfigPath,
12
+ excludedFiles,
13
+ }: {
14
+ targetPath: string
15
+ tsConfigPath: string
16
+ excludedFiles?: (string | RegExp)[]
17
+ }) => {
18
+ if (!fs.existsSync(targetPath)) {
19
+ return { discoveredRouterFiles: [], discoveredSourceFiles: [] }
20
+ }
21
+
22
+ const usersExcludedFiles = (excludedFiles ?? []).map((value) =>
23
+ typeof value === 'string' ? new RegExp(`${value}`) : value
24
+ )
25
+ const excludedPrefixes = [/^node_modules/, /^\./, /^dist/].concat(usersExcludedFiles ?? [])
26
+
27
+ const files = fs.readdirSync(targetPath, { recursive: true }).filter((filePath): filePath is string => {
28
+ if (typeof filePath !== 'string') {
29
+ return false
30
+ }
31
+
32
+ if (excludedPrefixes.some((p) => p.test(filePath))) {
33
+ return false
34
+ }
35
+
36
+ if (!filePath.endsWith('.ts')) {
37
+ return false
38
+ }
39
+ return true
40
+ })
41
+
42
+ const project = new Project({
43
+ tsConfigFilePath: tsConfigPath,
44
+ })
45
+
46
+ const allSourceFiles = files
47
+ .map((fileName) => {
48
+ const filePath = path.resolve(targetPath, fileName)
49
+ return {
50
+ fileName,
51
+ sourceFile: project.getSourceFile(filePath),
52
+ }
53
+ })
54
+ .filter(
55
+ (
56
+ file
57
+ ): file is Omit<typeof file, 'sourceFile'> & { sourceFile: NonNullable<typeof file['sourceFile']> } =>
58
+ !!file.sourceFile
59
+ )
60
+
61
+ const routersInFiles = allSourceFiles
62
+ .map((file) => {
63
+ const { fileName, sourceFile } = file
64
+ const routers = discoverRouters(sourceFile)
65
+ if (routers.named.length === 0 && routers.anonymous.length === 0) {
66
+ return null
67
+ }
68
+ return {
69
+ fileName,
70
+ sourceFile,
71
+ routers,
72
+ }
73
+ })
74
+ .filter((file): file is NonNullable<typeof file> => file !== null)
75
+
76
+ return {
77
+ discoveredRouterFiles: routersInFiles,
78
+ discoveredSourceFiles: allSourceFiles.map((file) => file.sourceFile),
79
+ }
80
+ }
@@ -0,0 +1,42 @@
1
+ import { Router as RenamedRouter } from '../../../router/Router'
2
+
3
+ const namedRouterVariable = new RenamedRouter()
4
+
5
+ namedRouterVariable.get('/api', () => {
6
+ return 'test-data'
7
+ })
8
+
9
+ const anotherRouterVariable = new RenamedRouter()
10
+ .use((_, next) => next())
11
+ .with(() => {
12
+ const user = { id: '123' }
13
+ return {
14
+ user,
15
+ }
16
+ })
17
+
18
+ anotherRouterVariable.get('/api', () => {
19
+ return 'more-test-data'
20
+ })
21
+
22
+ export default new RenamedRouter()
23
+ .get('/anonapi', () => {
24
+ return 'otherdata'
25
+ })
26
+ .get('/anonapitwo', () => {
27
+ return 'otherdata'
28
+ })
29
+ .get('/anonapithree', () => {
30
+ return 'otherdata'
31
+ })
32
+
33
+ // Exists to make sure that discovery module ignores unrelated objects
34
+ class Router {
35
+ value = 'foo'
36
+
37
+ setValue(val: string) {
38
+ this.value = val
39
+ }
40
+ }
41
+ const unrelatedObject = new Router()
42
+ unrelatedObject.setValue('bar')
@@ -0,0 +1,18 @@
1
+ import { SyntaxKind } from 'ts-morph'
2
+
3
+ import { loadTestData } from '../../../utils/loadTestData'
4
+ import { discoverRouters } from './discoverRouters'
5
+
6
+ describe('discoverRouters', () => {
7
+ const dataFile = loadTestData('discoverRouters.spec.data.ts')
8
+
9
+ it('discovers routers from single file correctly', () => {
10
+ const routers = discoverRouters(dataFile)
11
+
12
+ expect(routers.named.length).toEqual(2)
13
+ expect(routers.named[0]).toEqual('namedRouterVariable')
14
+ expect(routers.named[1]).toEqual('anotherRouterVariable')
15
+ expect(routers.anonymous.length).toEqual(1)
16
+ expect(routers.anonymous[0].getKind()).toEqual(SyntaxKind.CallExpression)
17
+ })
18
+ })
@@ -0,0 +1,39 @@
1
+ import { SourceFile, SyntaxKind } from 'ts-morph'
2
+
3
+ import { Router } from '../../../router/Router'
4
+ import { nameOf } from '../../../utils/nameOf'
5
+ import { discoverImportedName } from '../discoverImports/discoverImports'
6
+
7
+ export const discoverRouters = (sourceFile: SourceFile) => {
8
+ const routerClassName = discoverImportedName({
9
+ sourceFile,
10
+ originalName: nameOf(Router),
11
+ })
12
+
13
+ if (!routerClassName) {
14
+ return { named: [], anonymous: [] }
15
+ }
16
+
17
+ const routers = sourceFile
18
+ .getDescendantsOfKind(SyntaxKind.NewExpression)
19
+ .filter((exp) => exp.getFirstChildByKindOrThrow(SyntaxKind.Identifier).getText() === routerClassName)
20
+
21
+ const namedRouters = routers.filter((node) => !!node.getFirstAncestorByKind(SyntaxKind.VariableDeclaration))
22
+ const anonymousRouters = routers.filter(
23
+ (node) => !!node.getFirstAncestorByKind(SyntaxKind.ExportAssignment)
24
+ )
25
+
26
+ return {
27
+ named: namedRouters.map((node) =>
28
+ node
29
+ .getFirstAncestorByKindOrThrow(SyntaxKind.VariableDeclaration)
30
+ .getFirstChildByKindOrThrow(SyntaxKind.Identifier)
31
+ .getText()
32
+ ),
33
+ anonymous: anonymousRouters.map((node) =>
34
+ node
35
+ .getFirstAncestorByKindOrThrow(SyntaxKind.ExportAssignment)
36
+ .getFirstChildByKindOrThrow(SyntaxKind.CallExpression)
37
+ ),
38
+ }
39
+ }
@@ -0,0 +1,2 @@
1
+ export * from './discoverRouterFiles/discoverRouterFiles'
2
+ export * from './discoverRouters/discoverRouters'
@@ -0,0 +1,12 @@
1
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
2
+ import { ExposedModelData } from '../types'
3
+ import { getSchema, SchemaType } from './getSchema'
4
+
5
+ export const generateComponentSchemas = (models: ExposedModelData[]) => {
6
+ const schemas: Record<string, SchemaType> = {}
7
+
8
+ models.forEach((model) => {
9
+ schemas[model.name] = getSchema(model.shape)
10
+ })
11
+ return schemas
12
+ }
@@ -0,0 +1,138 @@
1
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
2
+ import { ApiDocsPreferences } from '../manager/OpenApiManager'
3
+ import { EndpointData, PathDefinition } from '../types'
4
+ import { getSchema } from './getSchema'
5
+
6
+ export const generatePaths = (endpoints: EndpointData[], preferences: ApiDocsPreferences) => {
7
+ const paths: Record<
8
+ string,
9
+ {
10
+ get?: PathDefinition
11
+ post?: PathDefinition
12
+ put?: PathDefinition
13
+ patch?: PathDefinition
14
+ delete?: PathDefinition
15
+ }
16
+ > = {}
17
+
18
+ const { allowOptionalPathParams } = preferences
19
+
20
+ endpoints.forEach((endpoint) => {
21
+ const path = endpoint.path
22
+ .split('/')
23
+ .map((param) => {
24
+ if (param.startsWith(':')) {
25
+ return `{${param.substring(1).replace('?', '')}}`
26
+ }
27
+ return param
28
+ })
29
+ .join('/')
30
+
31
+ const pathParams = endpoint.requestPathParams.map((param) => ({
32
+ name: param.identifier,
33
+ in: 'path' as const,
34
+ description:
35
+ param.optional && !allowOptionalPathParams
36
+ ? `(Optional parameter) ${param.description}`
37
+ : param.description ?? '',
38
+ required: !allowOptionalPathParams || !param.optional,
39
+ schema: getSchema(param.signature),
40
+ }))
41
+
42
+ const queryParams = endpoint.requestQuery.map((param) => ({
43
+ name: param.identifier,
44
+ in: 'query' as const,
45
+ description: param.description ?? '',
46
+ required: !param.optional,
47
+ schema: getSchema(param.signature),
48
+ }))
49
+
50
+ const headerParams = endpoint.requestHeaders.map((param) => ({
51
+ name: param.identifier,
52
+ in: 'header' as const,
53
+ description: param.description ?? '',
54
+ required: !param.optional,
55
+ schema: getSchema(param.signature),
56
+ }))
57
+
58
+ const acceptedBodyTypes: Partial<
59
+ Record<'text/plain' | 'application/json' | 'application/x-www-form-urlencoded', unknown>
60
+ > = {}
61
+
62
+ if (endpoint.rawBody) {
63
+ acceptedBodyTypes['text/plain'] = {
64
+ schema: getSchema(endpoint.rawBody.signature),
65
+ }
66
+ }
67
+
68
+ if (endpoint.objectBody.length > 0) {
69
+ const properties: Record<string, unknown> = {}
70
+ endpoint.objectBody.forEach((prop) => {
71
+ properties[prop.identifier] = getSchema(prop.signature)
72
+ })
73
+ const required = endpoint.objectBody.filter((prop) => !prop.optional).map((prop) => prop.identifier)
74
+ const content = {
75
+ schema: {
76
+ type: 'object',
77
+ properties,
78
+ required: required.length > 0 ? required : undefined,
79
+ },
80
+ }
81
+ acceptedBodyTypes['application/json'] = content
82
+ acceptedBodyTypes['application/x-www-form-urlencoded'] = content
83
+ }
84
+
85
+ const requestsWithBody = ['POST', 'PATCH', 'PUT']
86
+ const requestBody =
87
+ requestsWithBody.includes(endpoint.method) && Object.keys(acceptedBodyTypes).length > 0
88
+ ? { content: acceptedBodyTypes }
89
+ : undefined
90
+
91
+ const responses: PathDefinition['responses'] = {}
92
+ endpoint.responses.forEach((response) => {
93
+ const status = String(response.status)
94
+
95
+ const existingSchemas = responses[status]?.['content']?.['application/json']['schema']['oneOf'] ?? []
96
+
97
+ const responseSchema = getSchema(response.signature)
98
+ const content = (() => {
99
+ if ('type' in responseSchema && (responseSchema.type === 'void' || responseSchema.type === 'null')) {
100
+ return undefined
101
+ }
102
+
103
+ return {
104
+ 'application/json': {
105
+ schema: {
106
+ oneOf: [...existingSchemas, getSchema(response.signature)],
107
+ },
108
+ },
109
+ }
110
+ })()
111
+
112
+ responses[status] = {
113
+ description: response.description || '',
114
+ content,
115
+ }
116
+ })
117
+
118
+ const definition: PathDefinition = {
119
+ operationId: endpoint.name,
120
+ summary: endpoint.summary,
121
+ description: endpoint.description ?? '',
122
+ parameters: ([] as PathDefinition['parameters'])
123
+ .concat(pathParams)
124
+ .concat(queryParams)
125
+ .concat(headerParams),
126
+ requestBody: requestBody,
127
+ responses: responses,
128
+ tags: endpoint.tags,
129
+ }
130
+
131
+ paths[path] = {
132
+ ...paths[path],
133
+ [endpoint.method.toLowerCase()]: definition,
134
+ }
135
+ })
136
+
137
+ return paths
138
+ }
@@ -0,0 +1,17 @@
1
+ import { OpenApiManager } from '../manager/OpenApiManager'
2
+ import { generateComponentSchemas } from './generateComponentSchemas'
3
+ import { generatePaths } from './generatePaths'
4
+
5
+ export const generateOpenApiSpec = (manager: OpenApiManager) => {
6
+ const header = manager.getHeader()
7
+ const endpoints = manager.getEndpoints()
8
+
9
+ return {
10
+ openapi: '3.1.0' as const,
11
+ info: header,
12
+ paths: generatePaths(endpoints, manager.getPreferences()),
13
+ components: {
14
+ schemas: generateComponentSchemas(manager.getExposedModels()),
15
+ },
16
+ }
17
+ }
@@ -0,0 +1,169 @@
1
+ import {
2
+ ShapeOfNumberLiteral,
3
+ ShapeOfProperty,
4
+ ShapeOfRecord,
5
+ ShapeOfRef,
6
+ ShapeOfStringLiteral,
7
+ ShapeOfTuple,
8
+ ShapeOfType,
9
+ ShapeOfUnion,
10
+ } from '../../openapi/analyzerModule/types'
11
+
12
+ export type SchemaType =
13
+ | { type: string }
14
+ | { type: string; properties: Record<string, SchemaType>; required: string[] }
15
+ | { oneOf: SchemaType[] }
16
+ | { allOf: SchemaType[] }
17
+ | { type: 'array'; items: SchemaType }
18
+ | { type: 'array'; items: SchemaType; minItems: number; maxItems: number }
19
+ | { type: 'object'; additionalProperties: SchemaType }
20
+ | { type: 'string'; enum: string[] }
21
+ | { type: 'string'; format: string }
22
+ | { type: 'number'; enum: string[] }
23
+ | { $ref: string }
24
+
25
+ export const getSchema = (shape: string | ShapeOfType[]): SchemaType => {
26
+ if (typeof shape === 'string' && shape === 'any') {
27
+ return generateAny()
28
+ }
29
+
30
+ if (typeof shape === 'string' && shape === 'circular') {
31
+ return generateAny()
32
+ }
33
+
34
+ if (typeof shape === 'string' && shape === 'null') {
35
+ return {
36
+ type: 'null',
37
+ }
38
+ }
39
+
40
+ if (typeof shape === 'string' && shape === 'Date') {
41
+ return {
42
+ type: 'string',
43
+ format: 'date-time',
44
+ }
45
+ }
46
+
47
+ if (typeof shape === 'string' && shape === 'bigint') {
48
+ return {
49
+ type: 'string',
50
+ format: 'bigint',
51
+ }
52
+ }
53
+
54
+ if (typeof shape === 'string') {
55
+ return {
56
+ type: shape,
57
+ }
58
+ }
59
+
60
+ if (shape.length === 0) {
61
+ return {
62
+ type: 'unknown_20',
63
+ }
64
+ }
65
+
66
+ const isStringLiteral = shape[0].role === 'literal_string'
67
+ if (isStringLiteral) {
68
+ const typedShape = shape[0] as ShapeOfStringLiteral
69
+ return {
70
+ type: 'string',
71
+ enum: [typedShape.shape],
72
+ }
73
+ }
74
+
75
+ const isNumberLiteral = shape[0].role === 'literal_number'
76
+ if (isNumberLiteral) {
77
+ const typedShape = shape[0] as ShapeOfNumberLiteral
78
+ return {
79
+ type: 'number',
80
+ enum: [typedShape.shape],
81
+ }
82
+ }
83
+
84
+ const isObject = shape[0].role === 'property'
85
+ if (isObject) {
86
+ const typedShapes = shape as ShapeOfProperty[]
87
+ const properties: Record<string, SchemaType> = {}
88
+ typedShapes.forEach((prop) => {
89
+ properties[prop.identifier] = getSchema(prop.shape)
90
+ })
91
+ const required = typedShapes.filter((prop) => !prop.optional).map((prop) => prop.identifier)
92
+ return {
93
+ type: 'object',
94
+ properties,
95
+ required: required.length > 0 ? required : undefined,
96
+ }
97
+ }
98
+
99
+ const isUnion = shape[0].role === 'union'
100
+ if (isUnion) {
101
+ const typedShape = shape[0] as ShapeOfUnion
102
+ return {
103
+ oneOf: typedShape.shape.map((unionEntry) => getSchema(unionEntry.shape)),
104
+ }
105
+ }
106
+
107
+ const isRecord = shape[0].role === 'record'
108
+ if (isRecord) {
109
+ const recordShape = shape[0] as ShapeOfRecord
110
+ return {
111
+ type: 'object',
112
+ additionalProperties: getSchema(recordShape.shape),
113
+ }
114
+ }
115
+
116
+ const isArray = shape[0].role === 'array'
117
+ if (isArray) {
118
+ return {
119
+ type: 'array',
120
+ items: getSchema(shape[0].shape),
121
+ }
122
+ }
123
+
124
+ const isRef = shape[0].role === 'ref'
125
+ if (isRef) {
126
+ const refShape = shape[0] as ShapeOfRef
127
+ return {
128
+ $ref: `#/components/schemas/${refShape.shape}`,
129
+ }
130
+ }
131
+
132
+ const isTuple = shape[0].role === 'tuple'
133
+ if (isTuple) {
134
+ const tupleShape = shape[0] as ShapeOfTuple
135
+ const tupleEntries = tupleShape.shape
136
+ return {
137
+ type: 'array',
138
+ items: {
139
+ oneOf: tupleEntries.map((entry) => getSchema(entry.shape)),
140
+ },
141
+ minItems: tupleEntries.length,
142
+ maxItems: tupleEntries.length,
143
+ }
144
+ }
145
+
146
+ return {
147
+ type: 'unknown_21',
148
+ }
149
+ }
150
+
151
+ const generateAny = () => ({
152
+ oneOf: [
153
+ {
154
+ type: 'string',
155
+ },
156
+ {
157
+ type: 'boolean',
158
+ },
159
+ {
160
+ type: 'number',
161
+ },
162
+ {
163
+ type: 'object',
164
+ },
165
+ {
166
+ type: 'array',
167
+ },
168
+ ],
169
+ })
@@ -0,0 +1,4 @@
1
+ export * from './generateComponentSchemas'
2
+ export * from './generatePaths'
3
+ export * from './generatorModule'
4
+ export * from './getSchema'