@f3liz/rescript-autogen-openapi 0.3.1

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 (72) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +111 -0
  3. package/lib/es6/src/Codegen.d.ts +28 -0
  4. package/lib/es6/src/Codegen.mjs +423 -0
  5. package/lib/es6/src/Types.d.ts +286 -0
  6. package/lib/es6/src/Types.mjs +20 -0
  7. package/lib/es6/src/bindings/Toposort.mjs +12 -0
  8. package/lib/es6/src/core/CodegenUtils.mjs +261 -0
  9. package/lib/es6/src/core/DocOverride.mjs +399 -0
  10. package/lib/es6/src/core/FileSystem.d.ts +4 -0
  11. package/lib/es6/src/core/FileSystem.mjs +78 -0
  12. package/lib/es6/src/core/IRBuilder.mjs +201 -0
  13. package/lib/es6/src/core/OpenAPIParser.mjs +168 -0
  14. package/lib/es6/src/core/Pipeline.d.ts +6 -0
  15. package/lib/es6/src/core/Pipeline.mjs +150 -0
  16. package/lib/es6/src/core/ReferenceResolver.mjs +41 -0
  17. package/lib/es6/src/core/Result.mjs +378 -0
  18. package/lib/es6/src/core/SchemaIR.mjs +425 -0
  19. package/lib/es6/src/core/SchemaIRParser.mjs +683 -0
  20. package/lib/es6/src/core/SchemaRefResolver.mjs +146 -0
  21. package/lib/es6/src/core/SchemaRegistry.mjs +92 -0
  22. package/lib/es6/src/core/SpecDiffer.mjs +251 -0
  23. package/lib/es6/src/core/SpecMerger.mjs +237 -0
  24. package/lib/es6/src/generators/ComponentSchemaGenerator.mjs +207 -0
  25. package/lib/es6/src/generators/DiffReportGenerator.mjs +155 -0
  26. package/lib/es6/src/generators/EndpointGenerator.mjs +173 -0
  27. package/lib/es6/src/generators/IRToSuryGenerator.mjs +543 -0
  28. package/lib/es6/src/generators/IRToTypeGenerator.mjs +592 -0
  29. package/lib/es6/src/generators/IRToTypeScriptGenerator.mjs +143 -0
  30. package/lib/es6/src/generators/ModuleGenerator.mjs +285 -0
  31. package/lib/es6/src/generators/SchemaCodeGenerator.mjs +77 -0
  32. package/lib/es6/src/generators/ThinWrapperGenerator.mjs +97 -0
  33. package/lib/es6/src/generators/TypeScriptDtsGenerator.mjs +172 -0
  34. package/lib/es6/src/generators/TypeScriptWrapperGenerator.mjs +145 -0
  35. package/lib/es6/src/types/CodegenError.d.ts +66 -0
  36. package/lib/es6/src/types/CodegenError.mjs +79 -0
  37. package/lib/es6/src/types/Config.d.ts +31 -0
  38. package/lib/es6/src/types/Config.mjs +42 -0
  39. package/lib/es6/src/types/GenerationContext.mjs +47 -0
  40. package/package.json +53 -0
  41. package/rescript.json +26 -0
  42. package/src/Codegen.res +231 -0
  43. package/src/Types.res +222 -0
  44. package/src/bindings/Toposort.res +16 -0
  45. package/src/core/CodegenUtils.res +180 -0
  46. package/src/core/DocOverride.res +504 -0
  47. package/src/core/FileSystem.res +63 -0
  48. package/src/core/IRBuilder.res +66 -0
  49. package/src/core/OpenAPIParser.res +144 -0
  50. package/src/core/Pipeline.res +52 -0
  51. package/src/core/ReferenceResolver.res +41 -0
  52. package/src/core/Result.res +187 -0
  53. package/src/core/SchemaIR.res +291 -0
  54. package/src/core/SchemaIRParser.res +454 -0
  55. package/src/core/SchemaRefResolver.res +143 -0
  56. package/src/core/SchemaRegistry.res +107 -0
  57. package/src/core/SpecDiffer.res +270 -0
  58. package/src/core/SpecMerger.res +245 -0
  59. package/src/generators/ComponentSchemaGenerator.res +210 -0
  60. package/src/generators/DiffReportGenerator.res +152 -0
  61. package/src/generators/EndpointGenerator.res +176 -0
  62. package/src/generators/IRToSuryGenerator.res +386 -0
  63. package/src/generators/IRToTypeGenerator.res +423 -0
  64. package/src/generators/IRToTypeScriptGenerator.res +77 -0
  65. package/src/generators/ModuleGenerator.res +363 -0
  66. package/src/generators/SchemaCodeGenerator.res +84 -0
  67. package/src/generators/ThinWrapperGenerator.res +124 -0
  68. package/src/generators/TypeScriptDtsGenerator.res +193 -0
  69. package/src/generators/TypeScriptWrapperGenerator.res +166 -0
  70. package/src/types/CodegenError.res +85 -0
  71. package/src/types/Config.res +95 -0
  72. package/src/types/GenerationContext.res +56 -0
@@ -0,0 +1,193 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+
3
+ // TypeScriptDtsGenerator.res - Generate TypeScript .d.ts definition files
4
+ open Types
5
+
6
+ let getFirstJsonSchema = (contentDict: dict<mediaType>): option<jsonSchema> =>
7
+ Dict.keysToArray(contentDict)
8
+ ->Array.get(0)
9
+ ->Option.flatMap(contentType => Dict.get(contentDict, contentType))
10
+ ->Option.flatMap(mediaType => mediaType.schema)
11
+
12
+ let generateTypeScriptType = (name, description, schema) => {
13
+ let (irType, _) = SchemaIRParser.parseJsonSchema(schema)
14
+ IRToTypeScriptGenerator.generateNamedType(~namedSchema={name, description, type_: irType})
15
+ }
16
+
17
+ // Generate TypeScript interface for request type
18
+ let generateRequestInterface = (~endpoint: endpoint, ~functionName) => {
19
+ let requestTypeName = `${CodegenUtils.toPascalCase(functionName)}Request`
20
+ endpoint.requestBody->Option.flatMap(body =>
21
+ getFirstJsonSchema(body.content)->Option.map(schema =>
22
+ generateTypeScriptType(requestTypeName, body.description, schema)
23
+ )
24
+ )
25
+ }
26
+
27
+ // Generate TypeScript interface for response type
28
+ let generateResponseInterface = (~endpoint: endpoint, ~functionName) => {
29
+ let responseTypeName = `${CodegenUtils.toPascalCase(functionName)}Response`
30
+ let successCodes = ["200", "201", "202", "204"]
31
+ let successResponse = successCodes
32
+ ->Array.filterMap(code => Dict.get(endpoint.responses, code))
33
+ ->Array.get(0)
34
+
35
+ successResponse
36
+ ->Option.flatMap(response =>
37
+ response.content
38
+ ->Option.flatMap(getFirstJsonSchema)
39
+ ->Option.map(schema => generateTypeScriptType(responseTypeName, response.description->Some, schema))
40
+ )
41
+ ->Option.getOr(`export type ${responseTypeName} = void;`)
42
+ }
43
+
44
+ // Generate method signature for endpoint in an interface
45
+ let generateMethodSignature = (~endpoint: endpoint, ~functionName) => {
46
+ let params = endpoint.requestBody->Option.isSome
47
+ ? `client: MisskeyClient, request: ${CodegenUtils.toPascalCase(functionName)}Request`
48
+ : "client: MisskeyClient"
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
66
+ }
67
+
68
+ // Generate .d.ts file for a module (grouped by tag)
69
+ let generateModuleDts = (~moduleName, ~endpoints: array<endpoint>) => {
70
+ let interfaces =
71
+ endpoints
72
+ ->Array.map(endpoint => {
73
+ let functionName = CodegenUtils.generateOperationName(
74
+ endpoint.operationId,
75
+ endpoint.path,
76
+ endpoint.method,
77
+ )
78
+ let requestPart =
79
+ generateRequestInterface(~endpoint, ~functionName)->Option.getOr("")
80
+ let responsePart = generateResponseInterface(~endpoint, ~functionName)
81
+ [requestPart, responsePart]->Array.filter(s => s != "")->Array.join("\n")
82
+ })
83
+ ->Array.join("\n\n")
84
+
85
+ let methodSignatures =
86
+ endpoints
87
+ ->Array.map(endpoint =>
88
+ generateMethodSignature(
89
+ ~endpoint,
90
+ ~functionName=CodegenUtils.generateOperationName(
91
+ endpoint.operationId,
92
+ endpoint.path,
93
+ endpoint.method,
94
+ ),
95
+ )
96
+ )
97
+ ->Array.join("\n")
98
+
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
119
+ }
120
+
121
+ // Generate ComponentSchemas.d.ts
122
+ let generateComponentSchemasDts = (~schemas: Dict.t<jsonSchema>) => {
123
+ let content =
124
+ Dict.toArray(schemas)
125
+ ->Array.map(((name, schema)) => generateTypeScriptType(name, schema.description, schema))
126
+ ->Array.join("\n\n")
127
+
128
+ `
129
+ |// TypeScript definitions for ComponentSchemas
130
+ |// Generated by @f3liz/rescript-autogen-openapi
131
+ |// DO NOT EDIT
132
+ |
133
+ |${content}
134
+ |`->CodegenUtils.trimMargin
135
+ }
136
+
137
+ // Generate main index.d.ts with MisskeyClient class
138
+ 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
157
+ }
158
+
159
+ // Generate all .d.ts files for a spec
160
+ let generate = (~spec: openAPISpec, ~endpoints, ~outputDir): Pipeline.generationOutput => {
161
+ let endpointsByTag = OpenAPIParser.groupByTag(endpoints)
162
+ let moduleNames = []
163
+ let files =
164
+ Dict.toArray(endpointsByTag)
165
+ ->Array.filterMap(((tag, tagEndpoints)) =>
166
+ if Array.length(tagEndpoints) > 0 {
167
+ let name = CodegenUtils.toPascalCase(tag)
168
+ moduleNames->Array.push(name)
169
+ Some({
170
+ FileSystem.path: FileSystem.makePath(outputDir, `types/${name}.d.ts`),
171
+ content: generateModuleDts(~moduleName=name, ~endpoints=tagEndpoints),
172
+ })
173
+ } else {
174
+ None
175
+ }
176
+ )
177
+
178
+ spec.components
179
+ ->Option.flatMap(c => c.schemas)
180
+ ->Option.forEach(schemas =>
181
+ files->Array.push({
182
+ path: FileSystem.makePath(outputDir, "types/ComponentSchemas.d.ts"),
183
+ content: generateComponentSchemasDts(~schemas=schemas),
184
+ })
185
+ )
186
+
187
+ files->Array.push({
188
+ path: FileSystem.makePath(outputDir, "types/index.d.ts"),
189
+ content: generateIndexDts(~moduleNames),
190
+ })
191
+
192
+ {files, warnings: []}
193
+ }
@@ -0,0 +1,166 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+
3
+ // TypeScriptWrapperGenerator.res - Generate TypeScript/JavaScript wrapper
4
+ open Types
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
26
+
27
+ let generateWrapperMjs = (~endpoints, ~generatedModulePath) => {
28
+ let endpointsByTag = OpenAPIParser.groupByTag(endpoints)
29
+ let tags = Dict.keysToArray(endpointsByTag)
30
+
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 =
40
+ tags
41
+ ->Array.map(tag => {
42
+ let moduleName = CodegenUtils.toPascalCase(tag)
43
+ let methods =
44
+ Dict.get(endpointsByTag, tag)
45
+ ->Option.getOr([])
46
+ ->Array.map(endpoint => {
47
+ let functionName = CodegenUtils.generateOperationName(
48
+ endpoint.operationId,
49
+ endpoint.path,
50
+ endpoint.method,
51
+ )
52
+ 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
+ | },`
60
+ })
61
+ ->Array.join("\n")
62
+ `
63
+ |export const ${moduleName} = {
64
+ |${methods}
65
+ |};`
66
+ })
67
+ ->Array.join("\n\n")
68
+
69
+ `
70
+ |// Generated wrapper
71
+ |${imports}
72
+ |
73
+ |${misskeyClientJsCode}
74
+ |
75
+ |${wrappers}
76
+ |`->CodegenUtils.trimMargin
77
+ }
78
+
79
+ let generateWrapperDts = (~endpoints) => {
80
+ let endpointsByTag = OpenAPIParser.groupByTag(endpoints)
81
+ let tags = Dict.keysToArray(endpointsByTag)
82
+
83
+ let imports =
84
+ tags
85
+ ->Array.map(tag => {
86
+ let moduleName = CodegenUtils.toPascalCase(tag)
87
+ let typesToImport =
88
+ Dict.get(endpointsByTag, tag)
89
+ ->Option.getOr([])
90
+ ->Array.flatMap(endpoint => {
91
+ let pascalName = CodegenUtils.toPascalCase(
92
+ CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method),
93
+ )
94
+ if endpoint.requestBody->Option.isSome {
95
+ [` ${pascalName}Request,`, ` ${pascalName}Response,`]
96
+ } else {
97
+ [` ${pascalName}Response,`]
98
+ }
99
+ })
100
+ ->Array.join("\n")
101
+ `import type {
102
+ ${typesToImport}
103
+ } from '../types/${moduleName}.d.ts';`
104
+ })
105
+ ->Array.join("\n")
106
+
107
+ let namespaces =
108
+ tags
109
+ ->Array.map(tag => {
110
+ let moduleName = CodegenUtils.toPascalCase(tag)
111
+ let functions =
112
+ Dict.get(endpointsByTag, tag)
113
+ ->Option.getOr([])
114
+ ->Array.map(endpoint => {
115
+ let functionName = CodegenUtils.generateOperationName(
116
+ endpoint.operationId,
117
+ endpoint.path,
118
+ endpoint.method,
119
+ )
120
+ 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
+ })
127
+ let requestParam = endpoint.requestBody->Option.isSome ? `, request: ${pascalName}Request` : ""
128
+ `${docComment} export function ${functionName}(client: MisskeyClient${requestParam}): Promise<${pascalName}Response>;`
129
+ })
130
+ ->Array.join("\n")
131
+ `
132
+ |export namespace ${moduleName} {
133
+ |${functions}
134
+ |}`
135
+ })
136
+ ->Array.join("\n\n")
137
+
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
150
+ }
151
+
152
+ let generate = (~endpoints, ~outputDir, ~generatedModulePath="../generated") => {
153
+ Pipeline.fromFilesAndWarnings(
154
+ [
155
+ {
156
+ FileSystem.path: FileSystem.makePath(outputDir, "wrapper/index.mjs"),
157
+ content: generateWrapperMjs(~endpoints, ~generatedModulePath),
158
+ },
159
+ {
160
+ path: FileSystem.makePath(outputDir, "wrapper/index.d.ts"),
161
+ content: generateWrapperDts(~endpoints),
162
+ },
163
+ ],
164
+ [],
165
+ )
166
+ }
@@ -0,0 +1,85 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+
3
+ // Error.res - Compact error and warning types with helpers
4
+
5
+ // Error context for debugging (defined here to avoid circular dependency)
6
+ @genType
7
+ type context = {
8
+ path: string,
9
+ operation: string,
10
+ schema: option<JSON.t>, // Use JSON.t to avoid circular dependency with Types.jsonSchema
11
+ }
12
+
13
+ // Structured error types (keep original names for backward compat)
14
+ @genType
15
+ type t =
16
+ | SpecResolutionError({url: string, message: string})
17
+ | SchemaParseError({context: context, reason: string})
18
+ | ReferenceError({ref: string, context: context})
19
+ | ValidationError({schema: string, input: JSON.t, issues: array<string>})
20
+ | CircularSchemaError({ref: string, depth: int, path: string})
21
+ | FileWriteError({filePath: string, message: string})
22
+ | InvalidConfigError({field: string, message: string})
23
+ | UnknownError({message: string, context: option<context>})
24
+
25
+ // Warning types
26
+ module Warning = {
27
+ @genType
28
+ type t =
29
+ | FallbackToJson({reason: string, context: context})
30
+ | UnsupportedFeature({feature: string, fallback: string, location: string})
31
+ | DepthLimitReached({depth: int, path: string})
32
+ | MissingSchema({ref: string, location: string})
33
+ | IntersectionNotFullySupported({location: string, note: string})
34
+ | ComplexUnionSimplified({location: string, types: string})
35
+
36
+ let toString = w =>
37
+ switch w {
38
+ | FallbackToJson({reason, context}) =>
39
+ `⚠️ Falling back to JSON.t at '${context.path}' (${context.operation}): ${reason}`
40
+ | UnsupportedFeature({feature, fallback, location}) =>
41
+ `⚠️ Unsupported feature '${feature}' at '${location}', using fallback: ${fallback}`
42
+ | DepthLimitReached({depth, path}) =>
43
+ `⚠️ Depth limit ${depth->Int.toString} reached at '${path}', using simplified type`
44
+ | MissingSchema({ref, location}) =>
45
+ `⚠️ Schema reference '${ref}' not found at '${location}'`
46
+ | IntersectionNotFullySupported({location, note}) =>
47
+ `⚠️ Intersection type at '${location}' not fully supported: ${note}`
48
+ | ComplexUnionSimplified({location, types}) =>
49
+ `⚠️ Complex union at '${location}' simplified (types: ${types})`
50
+ }
51
+
52
+ let print = warnings =>
53
+ if Array.length(warnings) > 0 {
54
+ Console.log("\n⚠️ Warnings:")
55
+ warnings->Array.forEach(w => Console.log(toString(w)))
56
+ }
57
+ }
58
+
59
+ // Error helpers
60
+ let toString = e =>
61
+ switch e {
62
+ | SpecResolutionError({url, message}) => `Failed to resolve spec from '${url}': ${message}`
63
+ | SchemaParseError({context, reason}) =>
64
+ `Failed to parse schema at '${context.path}' (${context.operation}): ${reason}`
65
+ | ReferenceError({ref, context}) =>
66
+ `Failed to resolve reference '${ref}' at '${context.path}' (${context.operation})`
67
+ | ValidationError({schema, issues}) =>
68
+ `Validation failed for schema '${schema}': ${issues->Array.join(", ")}`
69
+ | CircularSchemaError({ref, depth, path}) =>
70
+ `Circular schema detected for '${ref}' at depth ${depth->Int.toString} (path: ${path})`
71
+ | FileWriteError({filePath, message}) => `Failed to write file '${filePath}': ${message}`
72
+ | InvalidConfigError({field, message}) => `Invalid configuration for field '${field}': ${message}`
73
+ | UnknownError({message, context}) =>
74
+ switch context {
75
+ | Some(ctx) => `Unknown error at '${ctx.path}' (${ctx.operation}): ${message}`
76
+ | None => `Unknown error: ${message}`
77
+ }
78
+ }
79
+
80
+ // Create context helper
81
+ let makeContext = (~path, ~operation, ~schema=?, ()) => {
82
+ path,
83
+ operation,
84
+ schema,
85
+ }
@@ -0,0 +1,95 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+
3
+ // Config.res - Generation configuration types
4
+
5
+ @genType
6
+ type generationStrategy =
7
+ | Separate
8
+ | SharedBase
9
+
10
+ @genType
11
+ type breakingChangeHandling =
12
+ | Error
13
+ | Warn
14
+ | Ignore
15
+
16
+ @genType
17
+ type forkSpecConfig = {
18
+ name: string,
19
+ specPath: string,
20
+ }
21
+
22
+ @genType
23
+ type generationTargets = {
24
+ rescriptApi: bool, // Generate base ReScript API (always true by default)
25
+ rescriptWrapper: bool, // Generate ReScript thin wrapper (pipe-first)
26
+ typescriptDts: bool, // Generate .d.ts type definitions
27
+ typescriptWrapper: bool, // Generate TypeScript/JavaScript wrapper
28
+ }
29
+
30
+ @genType
31
+ type t = {
32
+ specPath: string,
33
+ forkSpecs: option<array<forkSpecConfig>>,
34
+ outputDir: string,
35
+ strategy: generationStrategy,
36
+ modulePerTag: bool,
37
+ includeTags: option<array<string>>,
38
+ excludeTags: option<array<string>>,
39
+ generateDiffReport: bool,
40
+ breakingChangeHandling: breakingChangeHandling,
41
+ generateDocOverrides: option<bool>,
42
+ docOverrideDir: option<string>,
43
+ targets: option<generationTargets>, // Generation targets (what to generate)
44
+ dtsOutputDir: option<string>, // Output directory for .d.ts files (default: "types")
45
+ wrapperOutputDir: option<string>, // Output directory for wrapper files (default: "wrapper")
46
+ baseInstanceName: option<string>, // Subdirectory name for base instance (e.g., "misskey-io")
47
+ baseModulePrefix: option<string>, // Module prefix for base instance (e.g., "MisskeyIo")
48
+ }
49
+
50
+ // Default configuration
51
+ @genType
52
+ let make = (
53
+ ~specPath,
54
+ ~outputDir,
55
+ ~strategy=SharedBase,
56
+ ~modulePerTag=true,
57
+ ~includeTags=?,
58
+ ~excludeTags=?,
59
+ ~generateDiffReport=true,
60
+ ~breakingChangeHandling=Warn,
61
+ ~forkSpecs=?,
62
+ ~generateDocOverrides=?,
63
+ ~docOverrideDir=?,
64
+ ~targets=?,
65
+ ~dtsOutputDir=?,
66
+ ~wrapperOutputDir=?,
67
+ ~baseInstanceName=?,
68
+ ~baseModulePrefix=?,
69
+ (),
70
+ ) => {
71
+ specPath,
72
+ outputDir,
73
+ strategy,
74
+ modulePerTag,
75
+ includeTags,
76
+ excludeTags,
77
+ generateDiffReport,
78
+ breakingChangeHandling,
79
+ forkSpecs,
80
+ generateDocOverrides,
81
+ docOverrideDir,
82
+ targets,
83
+ dtsOutputDir,
84
+ wrapperOutputDir,
85
+ baseInstanceName,
86
+ baseModulePrefix,
87
+ }
88
+
89
+ // Default generation targets
90
+ let defaultTargets = (): generationTargets => {
91
+ rescriptApi: true,
92
+ rescriptWrapper: false,
93
+ typescriptDts: false,
94
+ typescriptWrapper: false,
95
+ }
@@ -0,0 +1,56 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+
3
+ // GenerationContext.res - Shared context type for IR code generators
4
+
5
+ // Extracted auxiliary type that was too complex to inline
6
+ type extractedType = {
7
+ typeName: string,
8
+ irType: SchemaIR.irType,
9
+ isUnboxed: bool, // Needs @unboxed annotation (for variant types from mixed unions)
10
+ }
11
+
12
+ type t = {
13
+ mutable warnings: array<CodegenError.Warning.t>,
14
+ mutable extractedTypes: array<extractedType>,
15
+ mutable extractCounter: int,
16
+ path: string,
17
+ insideComponentSchemas: bool, // Whether we're generating inside ComponentSchemas module
18
+ availableSchemas: option<array<string>>, // Schemas available in current module (for fork schemas)
19
+ modulePrefix: string, // Module prefix for qualified references (e.g., "MisskeyIo")
20
+ selfRefName: option<string>, // Schema name for self-referential type detection (e.g., "DriveFolder")
21
+ }
22
+
23
+ let make = (~path, ~insideComponentSchemas=false, ~availableSchemas=?, ~modulePrefix="", ~selfRefName=?, ()): t => {
24
+ warnings: [],
25
+ extractedTypes: [],
26
+ extractCounter: 0,
27
+ path,
28
+ insideComponentSchemas,
29
+ availableSchemas,
30
+ modulePrefix,
31
+ selfRefName,
32
+ }
33
+
34
+ let addWarning = (ctx: t, warning: CodegenError.Warning.t): unit => {
35
+ ctx.warnings->Array.push(warning)
36
+ }
37
+
38
+ let extractType = (ctx: t, ~baseName: string, ~isUnboxed=false, irType: SchemaIR.irType): string => {
39
+ // Check if this irType was already extracted (avoid duplicates)
40
+ let existing = ctx.extractedTypes->Array.find(({irType: existingIr}: extractedType) =>
41
+ SchemaIR.equals(existingIr, irType)
42
+ )
43
+ switch existing {
44
+ | Some({typeName}) => typeName
45
+ | None =>
46
+ ctx.extractCounter = ctx.extractCounter + 1
47
+ // ReScript type names must start with lowercase
48
+ let lowerBaseName = switch baseName->String.charAt(0) {
49
+ | "" => "extracted"
50
+ | first => first->String.toLowerCase ++ baseName->String.sliceToEnd(~start=1)
51
+ }
52
+ let typeName = `${lowerBaseName}_${Int.toString(ctx.extractCounter)}`
53
+ ctx.extractedTypes->Array.push({typeName, irType, isUnboxed})
54
+ typeName
55
+ }
56
+ }