@buildvision/nest-sdk-generator 1.2.2 → 2.1.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 (74) hide show
  1. package/.eslintrc.js +63 -0
  2. package/README.md +2 -2
  3. package/build/analyzer/classdeps.js +1 -1
  4. package/build/analyzer/controller.d.ts +4 -4
  5. package/build/analyzer/controller.js +3 -3
  6. package/build/analyzer/controllers.d.ts +1 -1
  7. package/build/analyzer/controllers.js +10 -2
  8. package/build/analyzer/decorator.d.ts +2 -2
  9. package/build/analyzer/decorator.js +19 -19
  10. package/build/analyzer/extractor.d.ts +4 -4
  11. package/build/analyzer/extractor.js +6 -17
  12. package/build/analyzer/index.d.ts +1 -1
  13. package/build/analyzer/index.js +2 -2
  14. package/build/analyzer/methods.d.ts +7 -7
  15. package/build/analyzer/methods.js +6 -9
  16. package/build/analyzer/module.js +2 -2
  17. package/build/analyzer/params.d.ts +59 -17
  18. package/build/analyzer/params.js +150 -129
  19. package/build/analyzer/route.d.ts +5 -2
  20. package/build/analyzer/route.js +13 -7
  21. package/build/analyzer/typedeps.d.ts +6 -6
  22. package/build/analyzer/typedeps.js +3 -3
  23. package/build/bin.js +9 -13
  24. package/build/config.d.ts +29 -19
  25. package/build/config.js +2 -2
  26. package/build/generator/genmodules.d.ts +33 -10
  27. package/build/generator/genmodules.js +165 -113
  28. package/build/generator/gentypes.js +1 -1
  29. package/build/generator/index.d.ts +1 -1
  30. package/build/generator/index.js +23 -22
  31. package/build/generator/prettier.d.ts +1 -1
  32. package/build/generator/prettier.js +2 -2
  33. package/build/generator/sdk-interface.d.ts +1 -1
  34. package/build/generator/sdk-interface.js +5 -5
  35. package/build/logging.js +4 -2
  36. package/build/rtk-query-generator/genmodules.d.ts +54 -0
  37. package/build/rtk-query-generator/genmodules.js +252 -0
  38. package/build/rtk-query-generator/gentypes.d.ts +10 -0
  39. package/build/rtk-query-generator/gentypes.js +62 -0
  40. package/build/rtk-query-generator/index.d.ts +6 -0
  41. package/build/rtk-query-generator/index.js +84 -0
  42. package/build/rtk-query-generator/prettier.d.ts +16 -0
  43. package/build/rtk-query-generator/prettier.js +43 -0
  44. package/build/rtk-query-generator/sdk-interface.d.ts +1 -0
  45. package/build/rtk-query-generator/sdk-interface.js +36 -0
  46. package/build/tsconfig.tsbuildinfo +1 -1
  47. package/build/utils.js +1 -1
  48. package/bun.lockb +0 -0
  49. package/package.json +16 -5
  50. package/src/analyzer/classdeps.ts +3 -0
  51. package/src/analyzer/controller.ts +12 -6
  52. package/src/analyzer/controllers.ts +16 -1
  53. package/src/analyzer/extractor.ts +12 -23
  54. package/src/analyzer/index.ts +5 -2
  55. package/src/analyzer/methods.ts +12 -13
  56. package/src/analyzer/module.ts +4 -10
  57. package/src/analyzer/params.ts +212 -182
  58. package/src/analyzer/route.ts +10 -4
  59. package/src/analyzer/typedeps.ts +13 -11
  60. package/src/bin.ts +11 -13
  61. package/src/config.ts +35 -24
  62. package/src/generator/genmodules.ts +212 -129
  63. package/src/generator/gentypes.ts +1 -0
  64. package/src/generator/index.ts +23 -23
  65. package/src/generator/prettier.ts +4 -2
  66. package/src/logging.ts +4 -1
  67. package/src/rtk-query-generator/genmodules.ts +335 -0
  68. package/src/rtk-query-generator/gentypes.ts +74 -0
  69. package/src/rtk-query-generator/index.ts +92 -0
  70. package/src/rtk-query-generator/prettier.ts +48 -0
  71. package/src/rtk-query-generator/sdk-interface.ts +32 -0
  72. package/src/utils.ts +2 -2
  73. package/tsconfig.tsbuildinfo +1 -0
  74. package/src/analyzer/decorator.ts +0 -29
package/src/bin.ts CHANGED
@@ -1,30 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import * as path from 'path'
4
+
4
5
  import { analyzerCli } from './analyzer'
5
6
  import { config, configPath } from './config'
6
- import generatorCli from './generator'
7
+ import generateAPIClient from './generator'
7
8
  import { println } from './logging'
9
+ import generateRTKQueryEndpoints from './rtk-query-generator'
8
10
 
9
11
  async function main() {
10
12
  const started = Date.now()
11
13
 
12
14
  process.chdir(path.dirname(path.resolve(configPath)))
13
15
 
14
- switch (process.argv[3]) {
15
- case '--analyze':
16
- await analyzerCli(config)
17
- break
16
+ const sdkContent = await analyzerCli(config)
17
+
18
+ const outputType = process.argv[3]
18
19
 
19
- case '--generate':
20
- case undefined:
21
- const sdkContent = await analyzerCli(config)
22
- await generatorCli(config, sdkContent)
23
- break
20
+ if (outputType === '--generate') {
21
+ await generateAPIClient(config, sdkContent)
22
+ }
24
23
 
25
- default:
26
- console.error('ERROR: Unknown action provided (must be either "--analyze" or "--generate")')
27
- process.exit(1)
24
+ if (outputType === '--generate-rtk') {
25
+ await generateRTKQueryEndpoints(config, sdkContent)
28
26
  }
29
27
 
30
28
  println('{green}', '@ Done in ' + ((Date.now() - started) / 1000).toFixed(2) + 's')
package/src/config.ts CHANGED
@@ -1,29 +1,32 @@
1
- import chalk = require('chalk')
2
1
  import * as fs from 'fs'
3
2
  import * as path from 'path'
4
3
 
4
+ import chalk = require('chalk')
5
+
5
6
  /**
6
7
  * The configuration file's content
7
8
  * For details on what these options do, see the project's README
8
9
  */
9
10
  export interface Config {
10
- /** Enable verbose mode */
11
- readonly verbose?: boolean
12
-
13
- /** Disable colored output */
14
- readonly noColor?: boolean
15
-
16
11
  /** Path to the API's source directory */
17
12
  readonly apiInputPath: string
18
13
 
19
- /** Path to generate the SDK at */
20
- readonly sdkOutput: string
14
+ readonly controllerOutput?: {
15
+ exportName?: {
16
+ addSuffix?: string
17
+ removeSuffix?: string
18
+ }
19
+ fileName?: {
20
+ addSuffix?: string
21
+ removeSuffix?: string
22
+ }
23
+ }
21
24
 
22
- /** Path to the SDK interface file */
23
- readonly sdkInterfacePath: string
25
+ /** If the SDK interface file does not exist yet, create one automatically (enabled by default) */
26
+ readonly generateDefaultSdkInterface?: boolean
24
27
 
25
- /** List of magic types */
26
- readonly magicTypes?: MagicType[]
28
+ /** Write generation timestamp in each TypeScript file (enabled by default) */
29
+ readonly generateTimestamps?: boolean
27
30
 
28
31
  /** Show a JSON output */
29
32
  readonly jsonOutput?: string
@@ -31,23 +34,31 @@ export interface Config {
31
34
  /** Prettify the JSON output */
32
35
  readonly jsonPrettyOutput?: boolean
33
36
 
34
- /** Prettify the generated files (enabled by default) */
35
- readonly prettify?: boolean
37
+ /** List of magic types */
38
+ readonly magicTypes?: MagicType[]
39
+
40
+ /** Disable colored output */
41
+ readonly noColor?: boolean
42
+
43
+ /** If the output directory already exists, overwrite it (enabled by default) */
44
+ readonly overwriteOldOutputDir?: boolean
36
45
 
37
46
  /** Path to Prettier's configuration file */
38
47
  readonly prettierConfig?: string
39
48
 
40
- /** Path to custom tsconfig file */
41
- readonly tsconfigFile?: string
49
+ /** Prettify the generated files (enabled by default) */
50
+ readonly prettify?: boolean
42
51
 
43
- /** If the output directory already exists, overwrite it (enabled by default) */
44
- readonly overwriteOldOutputDir?: boolean
52
+ /** Path to the SDK interface file */
53
+ readonly sdkInterfacePath: string
45
54
 
46
- /** If the SDK interface file does not exist yet, create one automatically (enabled by default) */
47
- readonly generateDefaultSdkInterface?: boolean
55
+ /** Path to generate the SDK at */
56
+ readonly sdkOutputPath: string
57
+ /** Path to custom tsconfig file */
58
+ readonly tsconfigFile?: string
48
59
 
49
- /** Write generation timestamp in each TypeScript file (enabled by default) */
50
- readonly generateTimestamps?: boolean
60
+ /** Enable verbose mode */
61
+ readonly verbose?: boolean
51
62
  }
52
63
 
53
64
  /**
@@ -55,8 +66,8 @@ export interface Config {
55
66
  */
56
67
  export interface MagicType {
57
68
  readonly nodeModuleFilePath: string
58
- readonly typeName: string
59
69
  readonly placeholderContent: string
70
+ readonly typeName: string
60
71
  }
61
72
 
62
73
  /**
@@ -2,180 +2,231 @@
2
2
  * @file Generate SDK modules
3
3
  */
4
4
 
5
- import * as path from 'path'
5
+ import path = require('path')
6
+
7
+ import { SdkController } from '../analyzer/controller'
6
8
  import { SdkModules } from '../analyzer/controllers'
7
9
  import { SdkHttpMethod, SdkMethod } from '../analyzer/methods'
8
- import { SdkMethodParams } from '../analyzer/params'
9
- import { resolveRouteWith, unparseRoute } from '../analyzer/route'
10
+ import { SdkMethodParam, getParamResolvedTypes, isParamResolvedType } from '../analyzer/params'
11
+ import { paramsFromRoute, resolveRouteWith, unparseRoute } from '../analyzer/route'
10
12
  import { ResolvedTypeDeps, normalizeExternalFilePath } from '../analyzer/typedeps'
13
+ import { config } from '../config'
11
14
  import { panic } from '../logging'
12
15
 
16
+ export function replaceSuffix(value: string, { addSuffix = '', removeSuffix = '' }: { addSuffix?: string; removeSuffix?: string }) {
17
+ return value.replace(new RegExp(removeSuffix + '$'), '') + addSuffix
18
+ }
19
+
20
+ export function controllerExportName(controller: SdkController): string {
21
+ const suffixReplacement = config.controllerOutput?.exportName ?? {}
22
+ return replaceSuffix(controller.className, suffixReplacement)
23
+ }
24
+
25
+ export function controllerFileName(controller: SdkController): { ext: string; name: string } {
26
+ const { ext, name } = path.parse(controller.path)
27
+ const suffixReplacement = config.controllerOutput?.fileName ?? {}
28
+ const fileName = replaceSuffix(name, suffixReplacement)
29
+ return {
30
+ name: fileName,
31
+ ext: ext || '.ts',
32
+ }
33
+ }
34
+
35
+ export function controllerRelativePath({
36
+ controller,
37
+ inputRelativePath,
38
+ }: {
39
+ controller: SdkController
40
+ inputRelativePath: string
41
+ }): string {
42
+ return path.join(inputRelativePath, controller.path)
43
+ }
44
+
13
45
  /**
14
46
  * Generate the SDK's module and controllers files
15
47
  * @param modules
16
48
  * @returns
17
49
  */
18
- export function generateSdkModules(modules: SdkModules): Map<string, string> {
50
+ export function generateSdkModules({
51
+ inputRelativePath,
52
+ modules,
53
+ }: {
54
+ inputRelativePath: string
55
+ modules: SdkModules
56
+ }): Map<string, string> {
19
57
  /** Generated module files */
20
58
  const genFiles = new Map<string, string>()
21
59
 
60
+ /** Index file content: Exports all controllers */
61
+ const indexContent: string[] = []
62
+
22
63
  // Iterate over each module
23
64
  for (const [moduleName, controllers] of modules) {
24
65
  // Iterate over each of the module's controllers
25
- for (const [controllerName, controller] of controllers) {
26
- /** Generated controller's content */
27
- const out: string[] = []
66
+ for (const [_, controller] of controllers) {
67
+ const { ext: fileExt, name: fileName } = controllerFileName(controller)
68
+ genFiles.set(fileName + fileExt, generateController({ moduleName, controller, inputRelativePath }))
69
+ indexContent.push(`export { default as ${controllerExportName(controller)} } from "./${fileName}";`)
70
+ }
71
+ }
28
72
 
29
- out.push('/// Parent module: ' + moduleName)
30
- out.push(`/// Controller: "${controllerName}" registered as "${controller.registrationName}" (${controller.methods.length} routes)`)
31
- out.push('')
32
- out.push('import { request } from "../central";')
73
+ genFiles.set('index.ts', indexContent.join('\n'))
33
74
 
34
- const imports = new Map<string, string[]>()
75
+ return genFiles
76
+ }
35
77
 
36
- const depsToImport = new Array<ResolvedTypeDeps>()
78
+ export function generateController({
79
+ controller,
80
+ inputRelativePath,
81
+ moduleName,
82
+ }: {
83
+ controller: SdkController
84
+ inputRelativePath: string
85
+ moduleName: string
86
+ }): string {
87
+ const controllerName = controller.className
88
+ /** Generated controller's content */
89
+ const out: string[] = []
90
+
91
+ out.push('/// Module: ' + moduleName)
92
+ out.push(`/// Controller: ${controllerName}`)
93
+ out.push(`/// File Path: file:///./${controllerRelativePath({ controller, inputRelativePath })}`)
94
+ out.push('')
95
+ out.push('import { request } from "./central";')
37
96
 
38
- // Iterate over each controller
39
- for (const method of controller.methods.values()) {
40
- const { parameters: args, query, body } = method.params
97
+ const imports = new Map<string, string[]>()
41
98
 
42
- depsToImport.push(method.returnType)
99
+ const depsToImport = new Array<ResolvedTypeDeps>()
43
100
 
44
- if (args) {
45
- depsToImport.push(...args.values())
46
- }
101
+ // Iterate over each controller
102
+ for (const method of controller.methods.values()) {
103
+ const { bodyParams, queryParams, routeParams } = method.params
47
104
 
48
- if (query) {
49
- depsToImport.push(...query.values())
50
- }
105
+ depsToImport.push(method.returnType)
106
+ depsToImport.push(...getParamResolvedTypes(routeParams))
107
+ depsToImport.push(...getParamResolvedTypes(queryParams))
108
+ depsToImport.push(...getParamResolvedTypes(bodyParams))
109
+ }
51
110
 
52
- if (body) {
53
- if (body.full) {
54
- depsToImport.push(body.type)
55
- } else {
56
- depsToImport.push(...body.fields.values())
57
- }
58
- }
111
+ // Build the imports list
112
+ for (const dep of depsToImport) {
113
+ for (const [file, types] of dep.dependencies) {
114
+ let imported = imports.get(file)
115
+
116
+ if (!imported) {
117
+ imported = []
118
+ imports.set(file, imported)
59
119
  }
60
120
 
61
- // Build the imports list
62
- for (const dep of depsToImport) {
63
- for (const [file, types] of dep.dependencies) {
64
- let imported = imports.get(file)
65
-
66
- if (!imported) {
67
- imported = []
68
- imports.set(file, imported)
69
- }
70
-
71
- for (const typ of types) {
72
- if (!imported.includes(typ)) {
73
- imported.push(typ)
74
- }
75
- }
121
+ for (const typ of types) {
122
+ if (!imported.includes(typ)) {
123
+ imported.push(typ)
76
124
  }
77
125
  }
126
+ }
127
+ }
78
128
 
79
- for (const [file, types] of imports) {
80
- out.push(
81
- `import type { ${types.join(', ')} } from "../_types/${normalizeExternalFilePath(file.replace(/\\/g, '/')).replace(/\\/g, '/')}";`
82
- )
83
- }
129
+ for (const [file, types] of imports) {
130
+ out.push(
131
+ `import type { ${types.join(', ')} } from "../_types/${normalizeExternalFilePath(file.replace(/\\/g, '/')).replace(/\\/g, '/')}";`
132
+ )
133
+ }
84
134
 
85
- out.push('')
86
- out.push(`export default {`)
87
-
88
- const { mutations, queries } = controller.methods.reduce(
89
- (acc, method) => {
90
- if (method.httpMethod === SdkHttpMethod.Get) {
91
- acc.queries.push(method)
92
- } else {
93
- acc.mutations.push(method)
94
- }
95
-
96
- return acc
97
- },
98
- { mutations: [] as SdkMethod[], queries: [] as SdkMethod[] }
99
- );
100
-
101
- out.push(` queries: {`)
102
-
103
- for (const method of queries) {
104
- out.push(generateSdkMethod(method))
135
+ out.push('')
136
+ out.push(`export default {`)
137
+
138
+ const { mutations, queries } = controller.methods.reduce(
139
+ (acc, method) => {
140
+ if (method.httpMethod === SdkHttpMethod.Get) {
141
+ acc.queries.push(method)
142
+ } else {
143
+ acc.mutations.push(method)
105
144
  }
106
145
 
107
- out.push(' },')
146
+ return acc
147
+ },
148
+ { mutations: [] as SdkMethod[], queries: [] as SdkMethod[] }
149
+ )
108
150
 
109
- out.push(` mutations: {`)
110
-
111
- for (const method of mutations) {
112
- out.push(generateSdkMethod(method))
113
- }
151
+ out.push(` queries: {`)
114
152
 
115
- out.push(' },')
153
+ for (const method of queries) {
154
+ out.push(generateSdkMethod({ method, controller, inputRelativePath }))
155
+ }
116
156
 
117
- out.push('')
118
- out.push('};')
157
+ out.push(' },')
119
158
 
120
- genFiles.set(path.join(moduleName, controller.camelClassName + '.ts'), out.join('\n'))
121
- }
159
+ out.push(` mutations: {`)
122
160
 
123
- /** Generated module's content */
124
- const moduleContent: string[] = []
161
+ for (const method of mutations) {
162
+ out.push(generateSdkMethod({ method, controller, inputRelativePath }))
163
+ }
125
164
 
126
- moduleContent.push('/// Module name: ' + moduleName)
127
- moduleContent.push('')
165
+ out.push(' },')
128
166
 
129
- for (const controller of controllers.keys()) {
130
- moduleContent.push(`export { default as ${controller} } from "./${controller}";`)
131
- }
167
+ out.push('')
168
+ out.push('};')
169
+ return out.join('\n')
170
+ }
132
171
 
133
- // Generate the SDK module file
134
- genFiles.set(path.join(moduleName, 'index.ts'), moduleContent.join('\n'))
135
- }
172
+ export function generateSdkMethod({
173
+ controller,
174
+ inputRelativePath,
175
+ method,
176
+ }: {
177
+ controller: SdkController
178
+ inputRelativePath: string
179
+ method: SdkMethod
180
+ }): string {
181
+ const out = []
136
182
 
137
- return genFiles
183
+ out.push('')
184
+ out.push(` /**`)
185
+ out.push(` * ${method.httpMethod} ${unparseRoute(method.route)}`)
186
+ out.push(` * ${controller.className}.${method.name}`)
187
+ out.push(` * file:///./${controllerRelativePath({ controller, inputRelativePath })}`)
188
+ out.push(` */`)
189
+ out.push(`${generateSdkMethodSignature(method)} {`)
190
+ out.push(generateSdkMethodBody(method))
191
+ out.push(' },')
192
+ return out.join('\n')
138
193
  }
139
194
 
140
- export function generateSdkMethod(method: SdkMethod): string {
141
- const out = [];
195
+ export function generateSdkMethodSignature(method: SdkMethod): string {
142
196
  const ret = method.returnType.resolvedType
143
197
  const promised = ret.startsWith('Promise<') ? ret : `Promise<${ret}>`
198
+ const paramsType = generateSdkMethodParams(method)
144
199
 
145
- out.push('')
146
- out.push(` // ${method.httpMethod} @ ${unparseRoute(method.route)}`)
147
- out.push(` ${method.name}(${generateSdkMethodParams(method.params)}): ${promised} {`)
148
- out.push(generateCentralRequest(method).replace(/^/gm, ' '))
149
- out.push(' },')
150
- return out.join('\n')
200
+ return `${method.name}(${paramsType}): ${promised}`
151
201
  }
152
202
 
153
- /**
154
- * Generate the method parameters for a given SDK method
155
- * @param params
156
- * @returns
157
- */
158
- export function generateSdkMethodParams(params: SdkMethodParams): string {
159
- // List of parameters (e.g. `id` in `/get/:id`, analyzed from the usages of the `@Param` decorator)
160
- const parameters = params.parameters ? [...params.parameters].map(([name, type]) => `${name}: ${type.resolvedType}`) : []
161
-
162
- // List of query values (e.g. `id` in `?id=xxx`, analyzed from the usages of the `@Query` decorator)
163
- const query = params.query ? [...params.query].map(([name, type]) => `${name}: ${type.resolvedType}`) : []
164
-
165
- // Body's content (type used with the `@Body` decorator)
166
- const body = params.body
167
- ? params.body.full
168
- ? params.body.type.resolvedType
169
- : '{ ' + [...params.body.fields].map(([name, type]) => `${name}: ${type.resolvedType}`).join(', ') + ' }'
170
- : null
171
-
172
- // The ternary conditions below are made to eclipse useless parameters
173
- // For instance, if we're not expecting any query nor body, these two parameters can be omitted when calling the method
174
- return [
175
- `params: {${' ' + parameters.join(', ') + ' '}}${parameters.length === 0 && !body && query.length === 0 ? ' = {}' : ''}`,
176
- `body: ${body ?? '{}'}${!body && query.length === 0 ? ' = {}' : ''}`,
177
- `query: {${' ' + query.join(', ') + ' '}}${query.length === 0 ? ' = {}' : ''}`,
178
- ].join(', ')
203
+ export function generateSdkMethodParams(method: SdkMethod): string {
204
+ const { httpMethod, name, params } = method
205
+
206
+ if (httpMethod === SdkHttpMethod.Get && params.bodyParams) {
207
+ panic(`${httpMethod} ${name} should not have Body params`)
208
+ }
209
+ if (httpMethod !== SdkHttpMethod.Get && params.queryParams) {
210
+ panic(`${httpMethod} ${name} should not have Query params`)
211
+ }
212
+
213
+ const inputTypes = []
214
+
215
+ if (params.routeParams) {
216
+ inputTypes.push(methodParamsToString(params.routeParams))
217
+ }
218
+
219
+ if (params.queryParams) {
220
+ inputTypes.push(methodParamsToString(params.queryParams))
221
+ }
222
+
223
+ if (params.bodyParams) {
224
+ inputTypes.push(methodParamsToString(params.bodyParams))
225
+ }
226
+
227
+ const mergedInputType = inputTypes.join(' &\n')
228
+
229
+ return mergedInputType ? `params: ${mergedInputType}` : ''
179
230
  }
180
231
 
181
232
  /**
@@ -183,12 +234,44 @@ export function generateSdkMethodParams(params: SdkMethodParams): string {
183
234
  * @param method
184
235
  * @returns
185
236
  */
186
- export function generateCentralRequest(method: SdkMethod): string {
187
- const resolvedRoute = resolveRouteWith(method.route, (param) => '${params.' + param + '}')
237
+ export function generateSdkMethodBody(method: SdkMethod): string {
238
+ const output = []
239
+ const { httpMethod, route } = method
240
+ const routeParams = paramsFromRoute(route)
241
+ const resolvedRoute = resolveRouteWith(route, (param) => '${' + param + '}')
242
+ const hasAnyParams = method.params.routeParams || method.params.queryParams || method.params.bodyParams
188
243
 
189
244
  if (resolvedRoute instanceof Error) {
190
245
  panic('Internal error: failed to resolve route: ' + resolvedRoute.message)
191
246
  }
247
+ const routeParamsSpread = routeParams.length > 0 ? `${routeParams.join(', ')},` : ''
248
+
249
+ // Destructure parameters if they exist
250
+ if (hasAnyParams) {
251
+ output.push(`const { ${routeParamsSpread} ...rest } = params`)
252
+ }
192
253
 
193
- return `return request('${method.httpMethod}', \`${resolvedRoute}\`, body, query)`
254
+ const isGet = httpMethod === SdkHttpMethod.Get
255
+ const body = isGet || !hasAnyParams ? 'null' : 'rest'
256
+ const query = isGet && hasAnyParams ? 'rest' : '{}'
257
+
258
+ output.push(`return request('${httpMethod}', \`${resolvedRoute}\`, ${body}, ${query})`)
259
+ return output.join('\n')
260
+ }
261
+
262
+ export function methodParamsToString(params: SdkMethodParam): string {
263
+ if (params === null) {
264
+ return ''
265
+ }
266
+
267
+ if (isParamResolvedType(params)) {
268
+ return params.resolvedType
269
+ }
270
+
271
+ const output = ['{']
272
+ for (const [name, type] of Object.entries(params)) {
273
+ output.push(`${name}: ${type.resolvedType},`)
274
+ }
275
+ output.push('}')
276
+ return output.join('\n')
194
277
  }
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import * as path from 'path'
6
+
6
7
  import { TypesExtractorContent } from '../analyzer/extractor'
7
8
  import { normalizeExternalFilePath } from '../analyzer/typedeps'
8
9
 
@@ -4,47 +4,47 @@
4
4
 
5
5
  import * as fs from 'fs'
6
6
  import * as path from 'path'
7
+
7
8
  import { SdkContent } from '../analyzer'
8
9
  import { normalizeExternalFilePath } from '../analyzer/typedeps'
9
10
  import { Config } from '../config'
10
11
  import { debug, panic, println } from '../logging'
12
+
11
13
  import { generateSdkModules } from './genmodules'
12
14
  import { generateSdkTypeFiles } from './gentypes'
13
15
  import { findPrettierConfig, prettify } from './prettier'
14
16
  import { defaultSdkInterface } from './sdk-interface'
15
17
 
16
- export default async function generatorCli(config: Config, sdkContent: SdkContent): Promise<void> {
18
+ export default async function generateAPIClient(config: Config, sdkContent: SdkContent): Promise<void> {
17
19
  const prettifyOutput = config.prettify !== false
18
20
 
19
21
  if (!prettifyOutput) {
20
22
  debug('NOTE: files will not be prettified with Prettier')
21
23
  }
22
24
 
23
- const output = path.resolve(process.cwd(), config.sdkOutput)
25
+ const outputPath = path.resolve(process.cwd(), config.sdkOutputPath)
26
+ const inputPath = path.resolve(process.cwd(), config.apiInputPath)
27
+ const inputRelativePath = path.relative(outputPath, inputPath)
24
28
 
25
- if (fs.existsSync(output)) {
29
+ if (fs.existsSync(outputPath)) {
26
30
  if (config.overwriteOldOutputDir === false) {
27
31
  panic("Please provide an output directory that doesn't exist yet")
28
32
  } else {
29
- if (!fs.existsSync(path.join(output, 'central.ts'))) {
30
- panic("Provided output path exists but doesn't seem to contain an SDK output. Please check the output directory.")
31
- } else {
32
- fs.rmSync(output, { recursive: true })
33
- }
33
+ fs.rmSync(outputPath, { recursive: true })
34
34
  }
35
35
  }
36
36
 
37
- const outputParentDir = path.dirname(output)
37
+ const outputParentDir = path.dirname(outputPath)
38
38
 
39
39
  if (!fs.existsSync(outputParentDir)) {
40
40
  panic("Output directory's parent {magentaBright} does not exist.", outputParentDir)
41
41
  }
42
42
 
43
- fs.mkdirSync(output)
43
+ fs.mkdirSync(outputPath)
44
44
 
45
45
  const prettierConfig = prettifyOutput ? findPrettierConfig(config) : {}
46
46
 
47
- const writeScriptTo = (parentDir: null | string, file: string, utf8Content: string) => {
47
+ const writeScriptTo = async (parentDir: null | string, file: string, utf8Content: string) => {
48
48
  if (file.endsWith('.ts')) {
49
49
  utf8Content =
50
50
  '/// Auto-generated file (nest-sdk-generator)\n' +
@@ -54,35 +54,35 @@ export default async function generatorCli(config: Config, sdkContent: SdkConten
54
54
  utf8Content
55
55
  }
56
56
 
57
- const fullPath = path.resolve(output, parentDir ?? '', file)
57
+ const fullPath = path.resolve(outputPath, parentDir ?? '', file)
58
58
  fs.mkdirSync(path.dirname(fullPath), { recursive: true })
59
- fs.writeFileSync(
60
- fullPath,
61
- prettifyOutput ? prettify(utf8Content, prettierConfig, file.endsWith('.json') ? 'json' : 'typescript') : utf8Content,
62
- 'utf8'
63
- )
59
+
60
+ const fileContent = prettifyOutput
61
+ ? await prettify(utf8Content, prettierConfig, file.endsWith('.json') ? 'json' : 'typescript')
62
+ : utf8Content
63
+ fs.writeFileSync(fullPath, fileContent, 'utf8')
64
64
  }
65
65
 
66
66
  println('> Generating type files...')
67
67
 
68
68
  for (const [file, content] of generateSdkTypeFiles(sdkContent.types)) {
69
- writeScriptTo('_types', normalizeExternalFilePath(file), content)
69
+ await writeScriptTo('_types', normalizeExternalFilePath(file), content)
70
70
  }
71
71
 
72
72
  println('> Generating modules...')
73
-
74
- for (const [file, content] of generateSdkModules(sdkContent.modules)) {
75
- writeScriptTo(null, file, content)
73
+ const modules = sdkContent.modules
74
+ for (const [file, content] of generateSdkModules({ modules, inputRelativePath })) {
75
+ await writeScriptTo(null, file, content)
76
76
  }
77
77
 
78
78
  const sdkInterfacePath = path.resolve(process.cwd(), config.sdkInterfacePath)
79
79
 
80
80
  const relativeSdkInterfacePath = path
81
- .relative(output, sdkInterfacePath)
81
+ .relative(outputPath, sdkInterfacePath)
82
82
  .replace(/\\/g, '/')
83
83
  .replace(/\.([jt]sx?)$/, '')
84
84
 
85
- writeScriptTo(null, 'central.ts', `export { request } from "${relativeSdkInterfacePath}"`)
85
+ await writeScriptTo(null, 'central.ts', `export { request } from "${relativeSdkInterfacePath}"`)
86
86
 
87
87
  if (!fs.existsSync(sdkInterfacePath) && config.generateDefaultSdkInterface !== false) {
88
88
  println('├─ Generating default SDK interface...')