@buildvision/nest-sdk-generator 1.1.3
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/.prettierrc +6 -0
- package/LICENSE.md +9 -0
- package/README.md +353 -0
- package/build/analyzer/builtin.d.ts +2 -0
- package/build/analyzer/builtin.js +11 -0
- package/build/analyzer/classdeps.d.ts +6 -0
- package/build/analyzer/classdeps.js +27 -0
- package/build/analyzer/controller.d.ts +26 -0
- package/build/analyzer/controller.js +103 -0
- package/build/analyzer/controllers.d.ts +7 -0
- package/build/analyzer/controllers.js +79 -0
- package/build/analyzer/decorator.d.ts +10 -0
- package/build/analyzer/decorator.js +29 -0
- package/build/analyzer/extractor.d.ts +107 -0
- package/build/analyzer/extractor.js +349 -0
- package/build/analyzer/index.d.ts +16 -0
- package/build/analyzer/index.js +101 -0
- package/build/analyzer/methods.d.ts +42 -0
- package/build/analyzer/methods.js +115 -0
- package/build/analyzer/module.d.ts +11 -0
- package/build/analyzer/module.js +41 -0
- package/build/analyzer/params.d.ts +38 -0
- package/build/analyzer/params.js +163 -0
- package/build/analyzer/route.d.ts +53 -0
- package/build/analyzer/route.js +131 -0
- package/build/analyzer/typedeps.d.ts +49 -0
- package/build/analyzer/typedeps.js +86 -0
- package/build/bin.d.ts +2 -0
- package/build/bin.js +39 -0
- package/build/config.d.ts +44 -0
- package/build/config.js +31 -0
- package/build/generator/genmodules.d.ts +24 -0
- package/build/generator/genmodules.js +134 -0
- package/build/generator/gentypes.d.ts +10 -0
- package/build/generator/gentypes.js +62 -0
- package/build/generator/index.d.ts +6 -0
- package/build/generator/index.js +83 -0
- package/build/generator/prettier.d.ts +16 -0
- package/build/generator/prettier.js +43 -0
- package/build/generator/sdk-interface.d.ts +1 -0
- package/build/generator/sdk-interface.js +36 -0
- package/build/logging.d.ts +6 -0
- package/build/logging.js +37 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- package/build/utils.d.ts +9 -0
- package/build/utils.js +56 -0
- package/package.json +34 -0
- package/src/analyzer/builtin.ts +9 -0
- package/src/analyzer/classdeps.ts +37 -0
- package/src/analyzer/controller.ts +135 -0
- package/src/analyzer/controllers.ts +117 -0
- package/src/analyzer/decorator.ts +29 -0
- package/src/analyzer/extractor.ts +491 -0
- package/src/analyzer/index.ts +136 -0
- package/src/analyzer/methods.ts +166 -0
- package/src/analyzer/module.ts +56 -0
- package/src/analyzer/params.ts +241 -0
- package/src/analyzer/route.ts +156 -0
- package/src/analyzer/typedeps.ts +130 -0
- package/src/bin.ts +33 -0
- package/src/config.ts +89 -0
- package/src/generator/genmodules.ts +163 -0
- package/src/generator/gentypes.ts +73 -0
- package/src/generator/index.ts +92 -0
- package/src/generator/prettier.ts +46 -0
- package/src/generator/sdk-interface.ts +32 -0
- package/src/logging.ts +35 -0
- package/src/utils.ts +61 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Entrypoint of the SDK generator
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'fs'
|
|
6
|
+
import * as path from 'path'
|
|
7
|
+
import { SdkContent } from '../analyzer'
|
|
8
|
+
import { normalizeExternalFilePath } from '../analyzer/typedeps'
|
|
9
|
+
import { Config } from '../config'
|
|
10
|
+
import { debug, panic, println } from '../logging'
|
|
11
|
+
import { generateSdkModules } from './genmodules'
|
|
12
|
+
import { generateSdkTypeFiles } from './gentypes'
|
|
13
|
+
import { findPrettierConfig, prettify } from './prettier'
|
|
14
|
+
import { defaultSdkInterface } from './sdk-interface'
|
|
15
|
+
|
|
16
|
+
export default async function generatorCli(config: Config, sdkContent: SdkContent): Promise<void> {
|
|
17
|
+
const prettifyOutput = config.prettify !== false
|
|
18
|
+
|
|
19
|
+
if (!prettifyOutput) {
|
|
20
|
+
debug('NOTE: files will not be prettified with Prettier')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const output = path.resolve(process.cwd(), config.sdkOutput)
|
|
24
|
+
|
|
25
|
+
if (fs.existsSync(output)) {
|
|
26
|
+
if (config.overwriteOldOutputDir === false) {
|
|
27
|
+
panic("Please provide an output directory that doesn't exist yet")
|
|
28
|
+
} 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
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const outputParentDir = path.dirname(output)
|
|
38
|
+
|
|
39
|
+
if (!fs.existsSync(outputParentDir)) {
|
|
40
|
+
panic("Output directory's parent {magentaBright} does not exist.", outputParentDir)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fs.mkdirSync(output)
|
|
44
|
+
|
|
45
|
+
const prettierConfig = prettifyOutput ? findPrettierConfig(config) : {}
|
|
46
|
+
|
|
47
|
+
const writeScriptTo = (parentDir: null | string, file: string, utf8Content: string) => {
|
|
48
|
+
if (file.endsWith('.ts')) {
|
|
49
|
+
utf8Content =
|
|
50
|
+
'/// Auto-generated file (nest-sdk-generator)\n' +
|
|
51
|
+
'/// Please do not edit this file - re-generate the SDK using the generator instead.\n' +
|
|
52
|
+
(config.generateTimestamps !== false ? '/// Generated on: ' + new Date().toUTCString() + '\n' : '') +
|
|
53
|
+
'///\n' +
|
|
54
|
+
utf8Content
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const fullPath = path.resolve(output, parentDir ?? '', file)
|
|
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
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
println('> Generating type files...')
|
|
67
|
+
|
|
68
|
+
for (const [file, content] of generateSdkTypeFiles(sdkContent.types)) {
|
|
69
|
+
writeScriptTo('_types', normalizeExternalFilePath(file), content)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
println('> Generating modules...')
|
|
73
|
+
|
|
74
|
+
for (const [file, content] of generateSdkModules(sdkContent.modules)) {
|
|
75
|
+
writeScriptTo(null, file, content)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const sdkInterfacePath = path.resolve(process.cwd(), config.sdkInterfacePath)
|
|
79
|
+
|
|
80
|
+
const relativeSdkInterfacePath = path
|
|
81
|
+
.relative(output, sdkInterfacePath)
|
|
82
|
+
.replace(/\\/g, '/')
|
|
83
|
+
.replace(/\.([jt]sx?)$/, '')
|
|
84
|
+
|
|
85
|
+
writeScriptTo(null, 'central.ts', `export { request } from "${relativeSdkInterfacePath}"`)
|
|
86
|
+
|
|
87
|
+
if (!fs.existsSync(sdkInterfacePath) && config.generateDefaultSdkInterface !== false) {
|
|
88
|
+
println('├─ Generating default SDK interface...')
|
|
89
|
+
|
|
90
|
+
fs.writeFileSync(sdkInterfacePath, defaultSdkInterface, 'utf8')
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Interface for prettifying generated files
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'fs'
|
|
6
|
+
import * as prettier from 'prettier'
|
|
7
|
+
import { Config } from '../config'
|
|
8
|
+
import { panic } from '../logging'
|
|
9
|
+
import { findFileAbove } from '../utils'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Find a .prettierrc configuration file in the current directory or above
|
|
13
|
+
*/
|
|
14
|
+
export function findPrettierConfig(config: Config): object {
|
|
15
|
+
let prettierConfigPath = config.prettierConfig ?? findFileAbove('.prettierrc', config.sdkOutput)
|
|
16
|
+
|
|
17
|
+
if (!prettierConfigPath) {
|
|
18
|
+
return {}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!fs.existsSync(prettierConfigPath)) {
|
|
22
|
+
panic('Prettier configuration was not found at specified path {magenta}', prettierConfigPath)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const text = fs.readFileSync(prettierConfigPath, 'utf8')
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(text)
|
|
29
|
+
} catch (e) {
|
|
30
|
+
throw new Error('Failed to parse Prettier configuration: ' + e)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Prettify a TypeScript or JSON input
|
|
36
|
+
* @param source
|
|
37
|
+
* @param config
|
|
38
|
+
* @param parser
|
|
39
|
+
* @returns
|
|
40
|
+
*/
|
|
41
|
+
export function prettify(source: string, config: object, parser: 'typescript' | 'json'): string {
|
|
42
|
+
return prettier.format(source, {
|
|
43
|
+
parser,
|
|
44
|
+
...config,
|
|
45
|
+
})
|
|
46
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const defaultSdkInterface = `
|
|
2
|
+
export async function request(
|
|
3
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
|
|
4
|
+
uri: string,
|
|
5
|
+
body: unknown,
|
|
6
|
+
query: Record<string, string>
|
|
7
|
+
): Promise<unknown> {
|
|
8
|
+
const url = new URL('http://localhost:3000' + uri)
|
|
9
|
+
url.search = new URLSearchParams(query).toString()
|
|
10
|
+
|
|
11
|
+
const params: RequestInit = {
|
|
12
|
+
method,
|
|
13
|
+
// Required for content to be correctly parsed by NestJS
|
|
14
|
+
headers: { 'Content-Type': 'application/json' },
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Setting a body is forbidden on GET requests
|
|
18
|
+
if (method !== 'GET') {
|
|
19
|
+
params.body = JSON.stringify(body)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return fetch(url.toString(), params).then((res) => {
|
|
23
|
+
// Handle failed requests
|
|
24
|
+
if (!res.ok) {
|
|
25
|
+
throw Error(res.statusText)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return res.json()
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
`.trim()
|
package/src/logging.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as chalk from 'chalk'
|
|
2
|
+
import { config } from './config'
|
|
3
|
+
|
|
4
|
+
export function format(message: string, ...params: Array<number | string>): string {
|
|
5
|
+
return message.replace(
|
|
6
|
+
/\{(black|red|green|yellow|blue|magenta|cyan|white|gray|grey|blackBright|redBright|greenBright|yellowBright|blueBright|magentaBright|cyanBright|whiteBright|)\}/g,
|
|
7
|
+
(match, color) => {
|
|
8
|
+
const param = params.shift() ?? panic(`In message:\n> {}\nMissing parameter:\n> {}`, message, match)
|
|
9
|
+
return color && config.noColor !== false ? (chalk as any)[color](param) : param
|
|
10
|
+
}
|
|
11
|
+
)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function panic(message: string, ...params: Array<number | string>): never {
|
|
15
|
+
console.error(chalk.redBright('ERROR: ' + format(message, ...params)))
|
|
16
|
+
process.exit(1)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function unreachable(message: string, ...params: Array<number | string>): never {
|
|
20
|
+
panic(message, ...params)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function warn(message: string, ...params: Array<number | string>) {
|
|
24
|
+
console.warn(chalk.yellow(format(message, ...params)))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function println(message: string, ...params: Array<number | string>) {
|
|
28
|
+
console.log(format(message, ...params))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function debug(message: string, ...params: Array<number | string>) {
|
|
32
|
+
if (config.verbose) {
|
|
33
|
+
console.warn(chalk.cyan(format(message, ...params)))
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as fs from 'fs'
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
|
|
4
|
+
export function findFileAbove(pattern: string | RegExp, dir: string): string | null {
|
|
5
|
+
if (!path.isAbsolute(dir)) {
|
|
6
|
+
// Get an absolute path to allow getting its parent using path.dirname()
|
|
7
|
+
dir = path.resolve(process.cwd(), dir)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// The previous directory
|
|
11
|
+
// This is used to check if we reached the top-level directory ; for instance on Linux:
|
|
12
|
+
// > path.dirname('/') === '/'
|
|
13
|
+
// So if the previous directory and the current one are equal, this means we reached the top-level directory
|
|
14
|
+
let prevDir = dir
|
|
15
|
+
|
|
16
|
+
let items = []
|
|
17
|
+
|
|
18
|
+
// Search until we find the desired file
|
|
19
|
+
while ((items = fs.readdirSync(dir).filter((item) => (pattern instanceof RegExp ? pattern.exec(item) : pattern === item))).length === 0) {
|
|
20
|
+
// Get path to the parent directory
|
|
21
|
+
dir = path.dirname(dir)
|
|
22
|
+
|
|
23
|
+
// If the path is empty or equal to the previous path, we reached the top-level directory
|
|
24
|
+
if (!dir || prevDir === dir) {
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
prevDir = dir
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Success!
|
|
32
|
+
return path.resolve(dir, items[0])
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Find all files matching a pattern, recursively
|
|
37
|
+
* @param pattern The pattern to look for
|
|
38
|
+
* @param dir The root directory to start searching from
|
|
39
|
+
* @param relative Get relative paths instead of absolute paths (default: true)
|
|
40
|
+
* @returns A list of paths
|
|
41
|
+
*/
|
|
42
|
+
export function findFilesRecursive(pattern: string | RegExp, dir: string, relative = true): string[] {
|
|
43
|
+
const cwd = process.cwd()
|
|
44
|
+
|
|
45
|
+
function find(pattern: string | RegExp, rootDir: string, currDir: string): string[] {
|
|
46
|
+
return fs
|
|
47
|
+
.readdirSync(currDir)
|
|
48
|
+
.map((item) => {
|
|
49
|
+
const fullPath = path.resolve(currDir, item)
|
|
50
|
+
|
|
51
|
+
return fs.lstatSync(fullPath).isDirectory()
|
|
52
|
+
? find(pattern, rootDir, fullPath)
|
|
53
|
+
: (typeof pattern === 'string' ? item === pattern : pattern.exec(item))
|
|
54
|
+
? [relative ? path.relative(rootDir, fullPath) : path.resolve(cwd, currDir, item)]
|
|
55
|
+
: []
|
|
56
|
+
})
|
|
57
|
+
.flat()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return find(pattern, dir, dir)
|
|
61
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"strict": true,
|
|
4
|
+
"noImplicitAny": true,
|
|
5
|
+
"noImplicitOverride": true,
|
|
6
|
+
"noImplicitReturns": true,
|
|
7
|
+
"noImplicitThis": true,
|
|
8
|
+
"noFallthroughCasesInSwitch": true,
|
|
9
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
10
|
+
"useUnknownInCatchVariables": true,
|
|
11
|
+
|
|
12
|
+
"downlevelIteration": true,
|
|
13
|
+
"experimentalDecorators": true,
|
|
14
|
+
|
|
15
|
+
"declaration": true,
|
|
16
|
+
"moduleResolution": "node",
|
|
17
|
+
"module": "commonjs",
|
|
18
|
+
"lib": ["esnext"],
|
|
19
|
+
"target": "es6",
|
|
20
|
+
|
|
21
|
+
"inlineSourceMap": true,
|
|
22
|
+
"incremental": true
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
"include": ["src"]
|
|
26
|
+
}
|