@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.
- package/.eslintrc.js +63 -0
- package/README.md +2 -2
- package/build/analyzer/classdeps.js +1 -1
- package/build/analyzer/controller.d.ts +4 -4
- package/build/analyzer/controller.js +3 -3
- package/build/analyzer/controllers.d.ts +1 -1
- package/build/analyzer/controllers.js +10 -2
- package/build/analyzer/decorator.d.ts +2 -2
- package/build/analyzer/decorator.js +19 -19
- package/build/analyzer/extractor.d.ts +4 -4
- package/build/analyzer/extractor.js +6 -17
- package/build/analyzer/index.d.ts +1 -1
- package/build/analyzer/index.js +2 -2
- package/build/analyzer/methods.d.ts +7 -7
- package/build/analyzer/methods.js +6 -9
- package/build/analyzer/module.js +2 -2
- package/build/analyzer/params.d.ts +59 -17
- package/build/analyzer/params.js +150 -129
- package/build/analyzer/route.d.ts +5 -2
- package/build/analyzer/route.js +13 -7
- package/build/analyzer/typedeps.d.ts +6 -6
- package/build/analyzer/typedeps.js +3 -3
- package/build/bin.js +9 -13
- package/build/config.d.ts +29 -19
- package/build/config.js +2 -2
- package/build/generator/genmodules.d.ts +33 -10
- package/build/generator/genmodules.js +165 -113
- package/build/generator/gentypes.js +1 -1
- package/build/generator/index.d.ts +1 -1
- package/build/generator/index.js +23 -22
- package/build/generator/prettier.d.ts +1 -1
- package/build/generator/prettier.js +2 -2
- package/build/generator/sdk-interface.d.ts +1 -1
- package/build/generator/sdk-interface.js +5 -5
- package/build/logging.js +4 -2
- package/build/rtk-query-generator/genmodules.d.ts +54 -0
- package/build/rtk-query-generator/genmodules.js +252 -0
- package/build/rtk-query-generator/gentypes.d.ts +10 -0
- package/build/rtk-query-generator/gentypes.js +62 -0
- package/build/rtk-query-generator/index.d.ts +6 -0
- package/build/rtk-query-generator/index.js +84 -0
- package/build/rtk-query-generator/prettier.d.ts +16 -0
- package/build/rtk-query-generator/prettier.js +43 -0
- package/build/rtk-query-generator/sdk-interface.d.ts +1 -0
- package/build/rtk-query-generator/sdk-interface.js +36 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/utils.js +1 -1
- package/bun.lockb +0 -0
- package/package.json +16 -5
- package/src/analyzer/classdeps.ts +3 -0
- package/src/analyzer/controller.ts +12 -6
- package/src/analyzer/controllers.ts +16 -1
- package/src/analyzer/extractor.ts +12 -23
- package/src/analyzer/index.ts +5 -2
- package/src/analyzer/methods.ts +12 -13
- package/src/analyzer/module.ts +4 -10
- package/src/analyzer/params.ts +212 -182
- package/src/analyzer/route.ts +10 -4
- package/src/analyzer/typedeps.ts +13 -11
- package/src/bin.ts +11 -13
- package/src/config.ts +35 -24
- package/src/generator/genmodules.ts +212 -129
- package/src/generator/gentypes.ts +1 -0
- package/src/generator/index.ts +23 -23
- package/src/generator/prettier.ts +4 -2
- package/src/logging.ts +4 -1
- package/src/rtk-query-generator/genmodules.ts +335 -0
- package/src/rtk-query-generator/gentypes.ts +74 -0
- package/src/rtk-query-generator/index.ts +92 -0
- package/src/rtk-query-generator/prettier.ts +48 -0
- package/src/rtk-query-generator/sdk-interface.ts +32 -0
- package/src/utils.ts +2 -2
- package/tsconfig.tsbuildinfo +1 -0
- 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
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
break
|
|
16
|
+
const sdkContent = await analyzerCli(config)
|
|
17
|
+
|
|
18
|
+
const outputType = process.argv[3]
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
await generatorCli(config, sdkContent)
|
|
23
|
-
break
|
|
20
|
+
if (outputType === '--generate') {
|
|
21
|
+
await generateAPIClient(config, sdkContent)
|
|
22
|
+
}
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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
|
-
/**
|
|
23
|
-
readonly
|
|
25
|
+
/** If the SDK interface file does not exist yet, create one automatically (enabled by default) */
|
|
26
|
+
readonly generateDefaultSdkInterface?: boolean
|
|
24
27
|
|
|
25
|
-
/**
|
|
26
|
-
readonly
|
|
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
|
-
/**
|
|
35
|
-
readonly
|
|
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
|
-
/**
|
|
41
|
-
readonly
|
|
49
|
+
/** Prettify the generated files (enabled by default) */
|
|
50
|
+
readonly prettify?: boolean
|
|
42
51
|
|
|
43
|
-
/**
|
|
44
|
-
readonly
|
|
52
|
+
/** Path to the SDK interface file */
|
|
53
|
+
readonly sdkInterfacePath: string
|
|
45
54
|
|
|
46
|
-
/**
|
|
47
|
-
readonly
|
|
55
|
+
/** Path to generate the SDK at */
|
|
56
|
+
readonly sdkOutputPath: string
|
|
57
|
+
/** Path to custom tsconfig file */
|
|
58
|
+
readonly tsconfigFile?: string
|
|
48
59
|
|
|
49
|
-
/**
|
|
50
|
-
readonly
|
|
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
|
|
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 {
|
|
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(
|
|
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 [
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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
|
-
|
|
75
|
+
return genFiles
|
|
76
|
+
}
|
|
35
77
|
|
|
36
|
-
|
|
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
|
-
|
|
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
|
-
|
|
99
|
+
const depsToImport = new Array<ResolvedTypeDeps>()
|
|
43
100
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
101
|
+
// Iterate over each controller
|
|
102
|
+
for (const method of controller.methods.values()) {
|
|
103
|
+
const { bodyParams, queryParams, routeParams } = method.params
|
|
47
104
|
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
146
|
+
return acc
|
|
147
|
+
},
|
|
148
|
+
{ mutations: [] as SdkMethod[], queries: [] as SdkMethod[] }
|
|
149
|
+
)
|
|
108
150
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
for (const method of mutations) {
|
|
112
|
-
out.push(generateSdkMethod(method))
|
|
113
|
-
}
|
|
151
|
+
out.push(` queries: {`)
|
|
114
152
|
|
|
115
|
-
|
|
153
|
+
for (const method of queries) {
|
|
154
|
+
out.push(generateSdkMethod({ method, controller, inputRelativePath }))
|
|
155
|
+
}
|
|
116
156
|
|
|
117
|
-
|
|
118
|
-
out.push('};')
|
|
157
|
+
out.push(' },')
|
|
119
158
|
|
|
120
|
-
|
|
121
|
-
}
|
|
159
|
+
out.push(` mutations: {`)
|
|
122
160
|
|
|
123
|
-
|
|
124
|
-
|
|
161
|
+
for (const method of mutations) {
|
|
162
|
+
out.push(generateSdkMethod({ method, controller, inputRelativePath }))
|
|
163
|
+
}
|
|
125
164
|
|
|
126
|
-
|
|
127
|
-
moduleContent.push('')
|
|
165
|
+
out.push(' },')
|
|
128
166
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
167
|
+
out.push('')
|
|
168
|
+
out.push('};')
|
|
169
|
+
return out.join('\n')
|
|
170
|
+
}
|
|
132
171
|
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
|
187
|
-
const
|
|
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
|
-
|
|
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
|
}
|
package/src/generator/index.ts
CHANGED
|
@@ -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
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
57
|
+
const fullPath = path.resolve(outputPath, parentDir ?? '', file)
|
|
58
58
|
fs.mkdirSync(path.dirname(fullPath), { recursive: true })
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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(
|
|
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(
|
|
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...')
|