@f3liz/rescript-autogen-openapi 0.5.3 → 0.6.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.
@@ -75,10 +75,12 @@ let generateEndpointModule = (path, method, operation: Types.operation) => {
75
75
  }
76
76
  let schemasCode = generateOperationSchemas(operationId, operation)
77
77
 
78
- `${docComment}module ${CodegenUtils.toPascalCase(operationId)} = {
79
- ${schemasCode->CodegenUtils.indent(2)}
80
-
81
- let endpoint = "${path}"
82
- let method = #${methodStr}
83
- }`
78
+ Handlebars.render(Templates.endpointModule, {
79
+ "docComment": docComment,
80
+ "moduleName": CodegenUtils.toPascalCase(operationId),
81
+ "schemasCode": schemasCode->CodegenUtils.indent(2),
82
+ "path": path,
83
+ "methodStr": methodStr,
84
+ },
85
+ )
84
86
  }
@@ -3,23 +3,14 @@
3
3
  // ThinWrapperGenerator.res - Generate ReScript thin wrappers with pipe-first ergonomics
4
4
  open Types
5
5
 
6
- let clientTypeCode = `
7
- |type client = {
8
- | baseUrl: string,
9
- | token: option<string>,
10
- | fetch: ${CodegenUtils.fetchTypeSignature},
11
- |}
12
- |`->CodegenUtils.trimMargin
6
+ let clientTypeCode =
7
+ Handlebars.render(Templates.clientType, {"fetchTypeSignature": CodegenUtils.fetchTypeSignature})
13
8
 
14
9
  let generateConnectFunction = (title) =>
15
- `
16
- |/** Create a client for ${title} */
17
- |let connect = (~baseUrl: string, ~token: option<string>=?, ~fetch: ${CodegenUtils.fetchTypeSignature}, ()): client => {
18
- | baseUrl,
19
- | token,
20
- | fetch,
21
- |}
22
- |`->CodegenUtils.trimMargin
10
+ Handlebars.render(
11
+ Templates.connectFunction,
12
+ {"title": title, "fetchTypeSignature": CodegenUtils.fetchTypeSignature},
13
+ )
23
14
 
24
15
  let generateWrapperFunction = (~endpoint: endpoint, ~generatedModuleName: string) => {
25
16
  let operationName = CodegenUtils.generateOperationName(
@@ -41,8 +32,16 @@ let generateWrapperFunction = (~endpoint: endpoint, ~generatedModuleName: string
41
32
 
42
33
  let callArguments = hasRequestBody ? "~body=request, " : ""
43
34
 
44
- `${docComment} ${signature}: promise<${generatedModuleName}.${operationName}Response> =>
45
- ${generatedModuleName}.${operationName}(${callArguments}~fetch=client.fetch)`
35
+ Handlebars.render(
36
+ Templates.wrapperFunction,
37
+ {
38
+ "docComment": docComment,
39
+ "signature": signature,
40
+ "generatedModuleName": generatedModuleName,
41
+ "operationName": operationName,
42
+ "callArguments": callArguments,
43
+ },
44
+ )
46
45
  }
47
46
 
48
47
  let generateWrapper = (
@@ -109,13 +108,14 @@ let generateWrapper = (
109
108
  })
110
109
  ->Array.join("\n\n")
111
110
 
112
- let fileContent = `// Generated thin wrapper
113
-
114
- ${clientTypeCode}
115
-
116
- ${generateConnectFunction(spec.info.title)}
117
-
118
- ${modulesCode}`
111
+ let fileContent = Handlebars.render(
112
+ Templates.wrapperFile,
113
+ {
114
+ "clientTypeCode": clientTypeCode,
115
+ "connectFunctionCode": generateConnectFunction(spec.info.title),
116
+ "modulesCode": modulesCode,
117
+ },
118
+ )
119
119
 
120
120
  Pipeline.fromFilesAndWarnings(
121
121
  [{path: FileSystem.makePath(outputDir, `${wrapperModuleName}.res`), content: fileContent}],
@@ -47,22 +47,29 @@ let generateMethodSignature = (~endpoint: endpoint, ~functionName) => {
47
47
  ? `client: MisskeyClient, request: ${CodegenUtils.toPascalCase(functionName)}Request`
48
48
  : "client: MisskeyClient"
49
49
 
50
- let docLines = endpoint.summary->Option.mapOr([], summary => {
51
- let lines = [" /**", ` * ${summary}`]
52
- endpoint.description->Option.forEach(description => {
53
- if description != summary {
54
- lines->Array.push(` * ${description}`)
55
- }
56
- })
57
- lines->Array.push(" */")
58
- lines
59
- })
60
-
61
- let code = `
62
- |${docLines->Array.join("\n")}
63
- | ${functionName}(${params}): Promise<${CodegenUtils.toPascalCase(functionName)}Response>;`
64
-
65
- code->CodegenUtils.trimMargin
50
+ let docLines = switch (endpoint.summary, endpoint.description) {
51
+ | (None, None) => ""
52
+ | (Some(summary), None) => ` /** ${summary} */`
53
+ | (None, Some(desc)) => {
54
+ let lines = desc->String.split("\n")->Array.map(line => line == "" ? " *" : ` * ${line}`)
55
+ ` /**\n${lines->Array.join("\n")}\n */`
56
+ }
57
+ | (Some(summary), Some(desc)) if summary == desc => ` /** ${summary} */`
58
+ | (Some(summary), Some(desc)) => {
59
+ let descLines = desc->String.split("\n")->Array.map(line => line == "" ? " *" : ` * ${line}`)
60
+ ` /**\n * ${summary}\n *\n${descLines->Array.join("\n")}\n */`
61
+ }
62
+ }
63
+
64
+ Handlebars.render(
65
+ Templates.methodSignature,
66
+ {
67
+ "docLines": docLines,
68
+ "functionName": functionName,
69
+ "params": params,
70
+ "responsePascalName": CodegenUtils.toPascalCase(functionName),
71
+ },
72
+ )
66
73
  }
67
74
 
68
75
  // Generate .d.ts file for a module (grouped by tag)
@@ -96,26 +103,14 @@ let generateModuleDts = (~moduleName, ~endpoints: array<endpoint>) => {
96
103
  )
97
104
  ->Array.join("\n")
98
105
 
99
- let header = `
100
- |// TypeScript definitions for ${moduleName}
101
- |// Generated by @f3liz/rescript-autogen-openapi
102
- |// DO NOT EDIT
103
- |
104
- |import { MisskeyClient } from './index';
105
- |import * as ComponentSchemas from './ComponentSchemas';
106
- |`->CodegenUtils.trimMargin
107
-
108
- `
109
- |${header}
110
- |
111
- |${interfaces}
112
- |
113
- |export interface ${moduleName}Module {
114
- |${methodSignatures}
115
- |}
116
- |
117
- |export const ${moduleName}: ${moduleName}Module;
118
- |`->CodegenUtils.trimMargin
106
+ Handlebars.render(
107
+ Templates.moduleDts,
108
+ {
109
+ "moduleName": moduleName,
110
+ "interfaces": interfaces,
111
+ "methodSignatures": methodSignatures,
112
+ },
113
+ )
119
114
  }
120
115
 
121
116
  // Generate ComponentSchemas.d.ts
@@ -125,35 +120,20 @@ let generateComponentSchemasDts = (~schemas: Dict.t<jsonSchema>) => {
125
120
  ->Array.map(((name, schema)) => generateTypeScriptType(name, schema.description, schema))
126
121
  ->Array.join("\n\n")
127
122
 
128
- `
129
- |// TypeScript definitions for ComponentSchemas
130
- |// Generated by @f3liz/rescript-autogen-openapi
131
- |// DO NOT EDIT
132
- |
133
- |${content}
134
- |`->CodegenUtils.trimMargin
123
+ Handlebars.render(
124
+ Templates.componentSchemasDts,
125
+ {"content": content},
126
+ )
135
127
  }
136
128
 
137
129
  // Generate main index.d.ts with MisskeyClient class
138
130
  let generateIndexDts = (~moduleNames) => {
139
- let imports = moduleNames->Array.map(m => `import { ${m}Module } from './${m}';`)->Array.join("\n")
140
- let exports = moduleNames->Array.map(m => `export const ${m}: ${m}Module;`)->Array.join("\n")
141
-
142
- `
143
- |// TypeScript definitions
144
- |// Generated by @f3liz/rescript-autogen-openapi
145
- |// DO NOT EDIT
146
- |
147
- |${imports}
148
- |
149
- |export class MisskeyClient {
150
- | constructor(baseUrl: string, token?: string);
151
- | readonly baseUrl: string;
152
- | readonly token?: string;
153
- |}
154
- |
155
- |${exports}
156
- |`->CodegenUtils.trimMargin
131
+ let modules = moduleNames->Array.map(m => {
132
+ "importLine": `import { ${m}Module } from './${m}';`,
133
+ "exportLine": `export const ${m}: ${m}Module;`,
134
+ })
135
+
136
+ Handlebars.render(Templates.indexDts, {"modules": modules})
157
137
  }
158
138
 
159
139
  // Generate all .d.ts files for a spec
@@ -3,43 +3,22 @@
3
3
  // TypeScriptWrapperGenerator.res - Generate TypeScript/JavaScript wrapper
4
4
  open Types
5
5
 
6
- let misskeyClientJsCode = `
7
- |export class MisskeyClient {
8
- | constructor(baseUrl, token) {
9
- | this.baseUrl = baseUrl;
10
- | this.token = token;
11
- | }
12
- |
13
- | async _fetch(url, method, body) {
14
- | const headers = { 'Content-Type': 'application/json' };
15
- | if (this.token) {
16
- | headers['Authorization'] = \`Bearer \${this.token}\`;
17
- | }
18
- | const response = await fetch(this.baseUrl + url, {
19
- | method,
20
- | headers,
21
- | body: body ? JSON.stringify(body) : undefined,
22
- | });
23
- | return response.json();
24
- | }
25
- |}`->CodegenUtils.trimMargin
6
+ let misskeyClientJsCode = {
7
+ let body: string = %raw(`
8
+ "export class MisskeyClient {\n constructor(baseUrl, token) {\n this.baseUrl = baseUrl;\n this.token = token;\n }\n\n async _fetch(url, method, body) {\n const headers = { 'Content-Type': 'application/json' };\n if (this.token) {\n headers['Authorization'] = \x60Bearer \x24{this.token}\x60;\n }\n const response = await fetch(this.baseUrl + url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n return response.json();\n }\n}"
9
+ `)
10
+ body
11
+ }
26
12
 
27
13
  let generateWrapperMjs = (~endpoints, ~generatedModulePath) => {
28
14
  let endpointsByTag = OpenAPIParser.groupByTag(endpoints)
29
15
  let tags = Dict.keysToArray(endpointsByTag)
30
16
 
31
- let imports =
32
- tags
33
- ->Array.map(tag => {
34
- let moduleName = CodegenUtils.toPascalCase(tag)
35
- `import * as ${moduleName} from '${generatedModulePath}/${moduleName}.mjs';`
36
- })
37
- ->Array.join("\n")
38
-
39
- let wrappers =
17
+ let tagData =
40
18
  tags
41
19
  ->Array.map(tag => {
42
20
  let moduleName = CodegenUtils.toPascalCase(tag)
21
+ let importLine = `import * as ${moduleName} from '${generatedModulePath}/${moduleName}.mjs';`
43
22
  let methods =
44
23
  Dict.get(endpointsByTag, tag)
45
24
  ->Option.getOr([])
@@ -50,37 +29,36 @@ let generateWrapperMjs = (~endpoints, ~generatedModulePath) => {
50
29
  endpoint.method,
51
30
  )
52
31
  let hasRequestBody = endpoint.requestBody->Option.isSome
53
- let bodyArg = hasRequestBody ? "body: request, " : ""
54
- `
55
- | async ${functionName}(client${hasRequestBody ? ", request" : ""}) {
56
- | return ${moduleName}.${functionName}({
57
- | ${bodyArg}fetch: (url, method, body) => client._fetch(url, method, body)
58
- | });
59
- | },`
32
+ Handlebars.render(
33
+ Templates.wrapperMjsMethod,
34
+ {
35
+ "functionName": functionName,
36
+ "moduleName": moduleName,
37
+ "requestArg": hasRequestBody ? ", request" : "",
38
+ "bodyArg": hasRequestBody ? "body: request, " : "",
39
+ },
40
+ )
60
41
  })
61
42
  ->Array.join("\n")
62
- `
63
- |export const ${moduleName} = {
64
- |${methods}
65
- |};`
43
+
44
+ let namespace = Handlebars.render(
45
+ Templates.wrapperMjsNamespace,
46
+ {"moduleName": moduleName, "methods": methods},
47
+ )
48
+ {"importLine": importLine, "namespace": namespace}
66
49
  })
67
- ->Array.join("\n\n")
68
50
 
69
- `
70
- |// Generated wrapper
71
- |${imports}
72
- |
73
- |${misskeyClientJsCode}
74
- |
75
- |${wrappers}
76
- |`->CodegenUtils.trimMargin
51
+ Handlebars.render(
52
+ Templates.wrapperMjs,
53
+ {"tags": tagData, "clientCode": misskeyClientJsCode},
54
+ )
77
55
  }
78
56
 
79
57
  let generateWrapperDts = (~endpoints) => {
80
58
  let endpointsByTag = OpenAPIParser.groupByTag(endpoints)
81
59
  let tags = Dict.keysToArray(endpointsByTag)
82
60
 
83
- let imports =
61
+ let tagData =
84
62
  tags
85
63
  ->Array.map(tag => {
86
64
  let moduleName = CodegenUtils.toPascalCase(tag)
@@ -98,16 +76,8 @@ let generateWrapperDts = (~endpoints) => {
98
76
  }
99
77
  })
100
78
  ->Array.join("\n")
101
- `import type {
102
- ${typesToImport}
103
- } from '../types/${moduleName}.d.ts';`
104
- })
105
- ->Array.join("\n")
79
+ let importBlock = `import type {\n${typesToImport}\n} from '../types/${moduleName}.d.ts';`
106
80
 
107
- let namespaces =
108
- tags
109
- ->Array.map(tag => {
110
- let moduleName = CodegenUtils.toPascalCase(tag)
111
81
  let functions =
112
82
  Dict.get(endpointsByTag, tag)
113
83
  ->Option.getOr([])
@@ -118,35 +88,40 @@ ${typesToImport}
118
88
  endpoint.method,
119
89
  )
120
90
  let pascalName = CodegenUtils.toPascalCase(functionName)
121
- let docComment = endpoint.summary->Option.mapOr("", summary => {
122
- let descriptionPart = endpoint.description->Option.mapOr("", description =>
123
- description == summary ? "" : " - " ++ description
124
- )
125
- ` /** ${summary}${descriptionPart} */\n`
126
- })
91
+ let docComment = switch (endpoint.summary, endpoint.description) {
92
+ | (None, None) => ""
93
+ | (Some(summary), None) => ` /** ${summary} */\n`
94
+ | (None, Some(desc)) => {
95
+ let lines = desc->String.split("\n")->Array.map(line => line == "" ? " *" : ` * ${line}`)
96
+ ` /**\n${lines->Array.join("\n")}\n */\n`
97
+ }
98
+ | (Some(summary), Some(desc)) if summary == desc => ` /** ${summary} */\n`
99
+ | (Some(summary), Some(desc)) => {
100
+ let descLines = desc->String.split("\n")->Array.map(line => line == "" ? " *" : ` * ${line}`)
101
+ ` /**\n * ${summary}\n *\n${descLines->Array.join("\n")}\n */\n`
102
+ }
103
+ }
127
104
  let requestParam = endpoint.requestBody->Option.isSome ? `, request: ${pascalName}Request` : ""
128
- `${docComment} export function ${functionName}(client: MisskeyClient${requestParam}): Promise<${pascalName}Response>;`
105
+ Handlebars.render(
106
+ Templates.wrapperDtsFunction,
107
+ {
108
+ "docComment": docComment,
109
+ "functionName": functionName,
110
+ "requestParam": requestParam,
111
+ "pascalName": pascalName,
112
+ },
113
+ )
129
114
  })
130
115
  ->Array.join("\n")
131
- `
132
- |export namespace ${moduleName} {
133
- |${functions}
134
- |}`
116
+
117
+ let namespace = Handlebars.render(
118
+ Templates.wrapperDtsNamespace,
119
+ {"moduleName": moduleName, "functions": functions},
120
+ )
121
+ {"importBlock": importBlock, "namespace": namespace}
135
122
  })
136
- ->Array.join("\n\n")
137
123
 
138
- `
139
- |// Generated TypeScript definitions for wrapper
140
- |${imports}
141
- |
142
- |export class MisskeyClient {
143
- | constructor(baseUrl: string, token?: string);
144
- | readonly baseUrl: string;
145
- | readonly token?: string;
146
- |}
147
- |
148
- |${namespaces}
149
- |`->CodegenUtils.trimMargin
124
+ Handlebars.render(Templates.wrapperDts, {"tags": tagData})
150
125
  }
151
126
 
152
127
  let generate = (~endpoints, ~outputDir, ~generatedModulePath="../generated") => {