@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.
Files changed (69) hide show
  1. package/.prettierrc +6 -0
  2. package/LICENSE.md +9 -0
  3. package/README.md +353 -0
  4. package/build/analyzer/builtin.d.ts +2 -0
  5. package/build/analyzer/builtin.js +11 -0
  6. package/build/analyzer/classdeps.d.ts +6 -0
  7. package/build/analyzer/classdeps.js +27 -0
  8. package/build/analyzer/controller.d.ts +26 -0
  9. package/build/analyzer/controller.js +103 -0
  10. package/build/analyzer/controllers.d.ts +7 -0
  11. package/build/analyzer/controllers.js +79 -0
  12. package/build/analyzer/decorator.d.ts +10 -0
  13. package/build/analyzer/decorator.js +29 -0
  14. package/build/analyzer/extractor.d.ts +107 -0
  15. package/build/analyzer/extractor.js +349 -0
  16. package/build/analyzer/index.d.ts +16 -0
  17. package/build/analyzer/index.js +101 -0
  18. package/build/analyzer/methods.d.ts +42 -0
  19. package/build/analyzer/methods.js +115 -0
  20. package/build/analyzer/module.d.ts +11 -0
  21. package/build/analyzer/module.js +41 -0
  22. package/build/analyzer/params.d.ts +38 -0
  23. package/build/analyzer/params.js +163 -0
  24. package/build/analyzer/route.d.ts +53 -0
  25. package/build/analyzer/route.js +131 -0
  26. package/build/analyzer/typedeps.d.ts +49 -0
  27. package/build/analyzer/typedeps.js +86 -0
  28. package/build/bin.d.ts +2 -0
  29. package/build/bin.js +39 -0
  30. package/build/config.d.ts +44 -0
  31. package/build/config.js +31 -0
  32. package/build/generator/genmodules.d.ts +24 -0
  33. package/build/generator/genmodules.js +134 -0
  34. package/build/generator/gentypes.d.ts +10 -0
  35. package/build/generator/gentypes.js +62 -0
  36. package/build/generator/index.d.ts +6 -0
  37. package/build/generator/index.js +83 -0
  38. package/build/generator/prettier.d.ts +16 -0
  39. package/build/generator/prettier.js +43 -0
  40. package/build/generator/sdk-interface.d.ts +1 -0
  41. package/build/generator/sdk-interface.js +36 -0
  42. package/build/logging.d.ts +6 -0
  43. package/build/logging.js +37 -0
  44. package/build/tsconfig.tsbuildinfo +1 -0
  45. package/build/utils.d.ts +9 -0
  46. package/build/utils.js +56 -0
  47. package/package.json +34 -0
  48. package/src/analyzer/builtin.ts +9 -0
  49. package/src/analyzer/classdeps.ts +37 -0
  50. package/src/analyzer/controller.ts +135 -0
  51. package/src/analyzer/controllers.ts +117 -0
  52. package/src/analyzer/decorator.ts +29 -0
  53. package/src/analyzer/extractor.ts +491 -0
  54. package/src/analyzer/index.ts +136 -0
  55. package/src/analyzer/methods.ts +166 -0
  56. package/src/analyzer/module.ts +56 -0
  57. package/src/analyzer/params.ts +241 -0
  58. package/src/analyzer/route.ts +156 -0
  59. package/src/analyzer/typedeps.ts +130 -0
  60. package/src/bin.ts +33 -0
  61. package/src/config.ts +89 -0
  62. package/src/generator/genmodules.ts +163 -0
  63. package/src/generator/gentypes.ts +73 -0
  64. package/src/generator/index.ts +92 -0
  65. package/src/generator/prettier.ts +46 -0
  66. package/src/generator/sdk-interface.ts +32 -0
  67. package/src/logging.ts +35 -0
  68. package/src/utils.ts +61 -0
  69. 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
+ }