@nexus-rpc/gen-core 0.1.0-alpha0
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/dist/definition-schema.d.ts +29 -0
- package/dist/definition-schema.js +3 -0
- package/dist/generator.d.ts +53 -0
- package/dist/generator.js +128 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/language-csharp.d.ts +4 -0
- package/dist/language-csharp.js +107 -0
- package/dist/language-go.d.ts +15 -0
- package/dist/language-go.js +213 -0
- package/dist/language-java.d.ts +5 -0
- package/dist/language-java.js +111 -0
- package/dist/language-python.d.ts +4 -0
- package/dist/language-python.js +181 -0
- package/dist/language-typescript.d.ts +5 -0
- package/dist/language-typescript.js +179 -0
- package/dist/parser.d.ts +2 -0
- package/dist/parser.js +34 -0
- package/dist/render-adapter.d.ts +44 -0
- package/dist/render-adapter.js +88 -0
- package/dist/utility.d.ts +10 -0
- package/dist/utility.js +30 -0
- package/package.json +31 -0
- package/src/definition-schema.ts +22 -0
- package/src/generator.ts +222 -0
- package/src/index.ts +8 -0
- package/src/language-csharp.ts +190 -0
- package/src/language-go.ts +310 -0
- package/src/language-java.ts +191 -0
- package/src/language-python.ts +260 -0
- package/src/language-typescript.ts +288 -0
- package/src/parser.ts +47 -0
- package/src/render-adapter.ts +205 -0
- package/src/utility.ts +52 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConvenienceRenderer,
|
|
3
|
+
goOptions,
|
|
4
|
+
GoRenderer,
|
|
5
|
+
GoTargetLanguage,
|
|
6
|
+
Name,
|
|
7
|
+
Namer,
|
|
8
|
+
Type,
|
|
9
|
+
type LanguageName,
|
|
10
|
+
type OptionValues,
|
|
11
|
+
type RenderContext,
|
|
12
|
+
type RendererOptions,
|
|
13
|
+
type Sourcelike,
|
|
14
|
+
} from "quicktype-core";
|
|
15
|
+
import { splitDescription } from "./utility.js";
|
|
16
|
+
import {
|
|
17
|
+
type PreparedService,
|
|
18
|
+
type PreparedTypeReference,
|
|
19
|
+
} from "./generator.js";
|
|
20
|
+
import { RenderAdapter, type RenderAccessible } from "./render-adapter.js";
|
|
21
|
+
import { stringEscape } from "quicktype-core/dist/support/Strings.js";
|
|
22
|
+
import {
|
|
23
|
+
BooleanOption,
|
|
24
|
+
getOptionValues,
|
|
25
|
+
} from "quicktype-core/dist/RendererOptions/index.js";
|
|
26
|
+
import { primitiveValueTypeKinds } from "quicktype-core/dist/language/Golang/utils.js";
|
|
27
|
+
|
|
28
|
+
// Add options
|
|
29
|
+
export const goWithNexusOptions = {
|
|
30
|
+
primitivePointers: new BooleanOption(
|
|
31
|
+
"primitive-pointers",
|
|
32
|
+
"Use pointers for nullable primitives",
|
|
33
|
+
false,
|
|
34
|
+
),
|
|
35
|
+
...goOptions,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Change some defaults globally
|
|
39
|
+
goWithNexusOptions.justTypesAndPackage.definition.defaultValue = true;
|
|
40
|
+
|
|
41
|
+
export class GoLanguageWithNexus extends GoTargetLanguage {
|
|
42
|
+
override getOptions(): typeof goWithNexusOptions {
|
|
43
|
+
return goWithNexusOptions;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
protected override makeRenderer<Lang extends LanguageName = "go">(
|
|
47
|
+
renderContext: RenderContext,
|
|
48
|
+
untypedOptionValues: RendererOptions<Lang>,
|
|
49
|
+
): GoRenderer {
|
|
50
|
+
const adapter = new GoRenderAdapter(
|
|
51
|
+
super.makeRenderer(renderContext, untypedOptionValues),
|
|
52
|
+
untypedOptionValues,
|
|
53
|
+
);
|
|
54
|
+
adapter.assertValidOptions();
|
|
55
|
+
|
|
56
|
+
const options = getOptionValues(goWithNexusOptions, untypedOptionValues);
|
|
57
|
+
|
|
58
|
+
return adapter.makeRenderer({
|
|
59
|
+
emitSourceStructure(original) {
|
|
60
|
+
adapter.emitServices();
|
|
61
|
+
original();
|
|
62
|
+
adapter.render.finishFile(adapter.makeFileName());
|
|
63
|
+
},
|
|
64
|
+
emitTopLevel(original, t, name) {
|
|
65
|
+
// Do not emit __ALL_TYPES__ placeholder
|
|
66
|
+
if (name.firstProposedName(new Map()) == "__ALL_TYPES__") {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
original(t, name);
|
|
70
|
+
},
|
|
71
|
+
nullableGoType(original, t, withIssues) {
|
|
72
|
+
// If the kind is a primitive and primitive pointers disabled, just return goType
|
|
73
|
+
if (
|
|
74
|
+
!options.primitivePointers &&
|
|
75
|
+
primitiveValueTypeKinds.includes(t.kind)
|
|
76
|
+
) {
|
|
77
|
+
return adapter.render.goType(t, withIssues);
|
|
78
|
+
}
|
|
79
|
+
return original(t, withIssues);
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
type GoRenderAccessible = Omit<GoRenderer, "emitTypesAndSupport"> &
|
|
86
|
+
RenderAccessible & {
|
|
87
|
+
readonly _options: OptionValues<typeof goOptions>;
|
|
88
|
+
get haveNamedUnions(): boolean;
|
|
89
|
+
collectAllImports(): Set<string>;
|
|
90
|
+
emitSourceStructure(): void;
|
|
91
|
+
emitTopLevel(t: Type, name: Name): void;
|
|
92
|
+
goType(t: Type, withIssues?: boolean): Sourcelike;
|
|
93
|
+
namerForObjectProperty(): Namer;
|
|
94
|
+
nullableGoType(t: Type, withIssues: boolean): Sourcelike;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
class GoRenderAdapter extends RenderAdapter<GoRenderAccessible> {
|
|
98
|
+
private _imports?: Record<string, string>;
|
|
99
|
+
|
|
100
|
+
assertValidOptions() {
|
|
101
|
+
// Currently, we only support single-file in Go
|
|
102
|
+
if (this.render._options.multiFileOutput) {
|
|
103
|
+
throw new Error("Multi-file output for Go not supported at this time");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
makeFileName() {
|
|
108
|
+
// If there is a single service, use that, otherwise use the
|
|
109
|
+
// filename sans extensions to build it
|
|
110
|
+
const services = Object.entries(this.schema.services);
|
|
111
|
+
const name =
|
|
112
|
+
services.length == 1
|
|
113
|
+
? `${services[0][0]}.go`
|
|
114
|
+
: `${this.nexusRendererOptions.firstFilenameSansExtensions}.go`;
|
|
115
|
+
return name.toLowerCase();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Key is qualified import, value is alias
|
|
119
|
+
get imports(): Record<string, string> {
|
|
120
|
+
if (this._imports === undefined) {
|
|
121
|
+
this._imports = {};
|
|
122
|
+
|
|
123
|
+
// Quicktype does not have a sophisticated import+alias construct for its
|
|
124
|
+
// renderer because they only ever needed one import. However, since we allow
|
|
125
|
+
// external types, we must support proper aliasing.
|
|
126
|
+
const origImports = this.render.collectAllImports();
|
|
127
|
+
if (
|
|
128
|
+
!this.render._options.justTypes &&
|
|
129
|
+
!this.render._options.justTypesAndPackage
|
|
130
|
+
) {
|
|
131
|
+
if (
|
|
132
|
+
this.render.haveNamedUnions &&
|
|
133
|
+
!this.render._options.multiFileOutput
|
|
134
|
+
) {
|
|
135
|
+
origImports.add("bytes");
|
|
136
|
+
origImports.add("errors");
|
|
137
|
+
}
|
|
138
|
+
origImports.add("encoding/json");
|
|
139
|
+
}
|
|
140
|
+
origImports.add("github.com/nexus-rpc/sdk-go/nexus");
|
|
141
|
+
for (const mport of origImports) {
|
|
142
|
+
this._imports[mport] = mport.split("/").pop()!;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Add any external type reference pre-last-dots as imports
|
|
146
|
+
// TODO(cretz): Generics with qualified type args that need to be imported?
|
|
147
|
+
const externalTypes = Object.values(this.schema.services).flatMap((svc) =>
|
|
148
|
+
Object.values(svc.operations).flatMap((op) => [
|
|
149
|
+
op?.input?.kind == "existing" ? op.input.name : null,
|
|
150
|
+
op?.output?.kind == "existing" ? op.output.name : null,
|
|
151
|
+
]),
|
|
152
|
+
);
|
|
153
|
+
for (const externalType of externalTypes) {
|
|
154
|
+
const lastDot = externalType?.lastIndexOf(".") ?? -1;
|
|
155
|
+
if (externalType && lastDot > 0) {
|
|
156
|
+
const mport = externalType.slice(0, lastDot);
|
|
157
|
+
if (!Object.hasOwn(this._imports, mport)) {
|
|
158
|
+
// Append number until an unused alias is found
|
|
159
|
+
const origAlias = mport.split("/").pop()!;
|
|
160
|
+
let alias = origAlias;
|
|
161
|
+
let number_ = 0;
|
|
162
|
+
while (Object.values(this._imports).includes(alias)) {
|
|
163
|
+
alias = `${origAlias}${++number_}`;
|
|
164
|
+
}
|
|
165
|
+
this._imports[mport] = alias;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return this._imports;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
emitServices() {
|
|
174
|
+
// Package decl
|
|
175
|
+
if (
|
|
176
|
+
!this.render._options.justTypes ||
|
|
177
|
+
this.render._options.justTypesAndPackage
|
|
178
|
+
) {
|
|
179
|
+
this.render.emitLineOnce(
|
|
180
|
+
"// Code generated by nexus-rpc-gen. DO NOT EDIT.",
|
|
181
|
+
);
|
|
182
|
+
this.render.ensureBlankLine();
|
|
183
|
+
const packageDeclaration = `package ${this.render._options.packageName}`;
|
|
184
|
+
this.render.emitLineOnce(packageDeclaration);
|
|
185
|
+
this.render.ensureBlankLine();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Emit imports. To match goimports, we do all non-dot sorted, followed by blank line,
|
|
189
|
+
// then all dotted, sorted
|
|
190
|
+
const imports = Object.entries(this.imports).toSorted(
|
|
191
|
+
([mportA, _a], [mportB, _b]) => mportA.localeCompare(mportB),
|
|
192
|
+
);
|
|
193
|
+
for (const [mport, alias] of imports.filter(
|
|
194
|
+
([mport, _]) => !mport.includes("."),
|
|
195
|
+
)) {
|
|
196
|
+
const aliasPiece = alias != mport.split("/").pop()! ? `${alias} ` : "";
|
|
197
|
+
// Must be a single string so it caches the full line for "once" so the
|
|
198
|
+
// Quicktype renderer doesn't render its own forms
|
|
199
|
+
this.render.emitLineOnce(`import ${aliasPiece}"${mport}"`);
|
|
200
|
+
}
|
|
201
|
+
this.render.ensureBlankLine();
|
|
202
|
+
for (const [mport, alias] of imports.filter(([mport, _]) =>
|
|
203
|
+
mport.includes("."),
|
|
204
|
+
)) {
|
|
205
|
+
const aliasPiece = alias != mport.split("/").pop()! ? `${alias} ` : "";
|
|
206
|
+
this.render.emitLineOnce(`import ${aliasPiece}"${mport}"`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Emit each service
|
|
210
|
+
for (const [serviceName, serviceSchema] of Object.entries(
|
|
211
|
+
this.schema.services,
|
|
212
|
+
)) {
|
|
213
|
+
this.emitService(serviceName, serviceSchema);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
emitService(serviceName: string, serviceSchema: PreparedService) {
|
|
218
|
+
this.render.ensureBlankLine();
|
|
219
|
+
|
|
220
|
+
const variableName = this.makeServiceTypeName(
|
|
221
|
+
this.render.makeNamedTypeNamer().nameStyle(serviceName),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// Collect operations
|
|
225
|
+
const fieldNamesInUse = {};
|
|
226
|
+
const operations: {
|
|
227
|
+
descPieces: string[];
|
|
228
|
+
opName: string;
|
|
229
|
+
fieldName: string;
|
|
230
|
+
inType: Sourcelike;
|
|
231
|
+
outType: Sourcelike;
|
|
232
|
+
}[] = Object.entries(serviceSchema.operations).flatMap(
|
|
233
|
+
([opName, opSchema]) => ({
|
|
234
|
+
descPieces: splitDescription(opSchema.description) ?? [],
|
|
235
|
+
opName,
|
|
236
|
+
fieldName: this.makeOperationFunctionName(
|
|
237
|
+
this.render.namerForObjectProperty().nameStyle(opName),
|
|
238
|
+
fieldNamesInUse,
|
|
239
|
+
),
|
|
240
|
+
inType: this.getNexusType(opSchema.input) ?? "nexus.NoValue",
|
|
241
|
+
outType: this.getNexusType(opSchema.output) ?? "nexus.NoValue",
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// Create var with anonymous struct
|
|
246
|
+
this.render.emitDescription(splitDescription(serviceSchema.description));
|
|
247
|
+
this.render.emitLine("var ", variableName, " = struct {");
|
|
248
|
+
this.render.indent(() => {
|
|
249
|
+
this.render.emitTable([
|
|
250
|
+
[["ServiceName", " "], ["string"]],
|
|
251
|
+
...operations.flatMap((op) => {
|
|
252
|
+
const pieces = [];
|
|
253
|
+
if (op.descPieces.length > 0) {
|
|
254
|
+
pieces.push([op.descPieces.map((d) => `// ${d}`)]);
|
|
255
|
+
}
|
|
256
|
+
pieces.push([
|
|
257
|
+
[op.fieldName, " "],
|
|
258
|
+
["nexus.OperationReference[", op.inType, ", ", op.outType, "]"],
|
|
259
|
+
]);
|
|
260
|
+
return pieces;
|
|
261
|
+
}),
|
|
262
|
+
]);
|
|
263
|
+
});
|
|
264
|
+
this.render.emitLine("}{");
|
|
265
|
+
this.render.indent(() => {
|
|
266
|
+
this.render.emitTable([
|
|
267
|
+
[["ServiceName:", " "], [`"${stringEscape(serviceName)}",`]],
|
|
268
|
+
...operations.map((op) => [
|
|
269
|
+
[op.fieldName + ":", " "],
|
|
270
|
+
[
|
|
271
|
+
"nexus.NewOperationReference[",
|
|
272
|
+
op.inType,
|
|
273
|
+
", ",
|
|
274
|
+
op.outType,
|
|
275
|
+
`]("${stringEscape(op.opName)}"),`,
|
|
276
|
+
],
|
|
277
|
+
]),
|
|
278
|
+
]);
|
|
279
|
+
});
|
|
280
|
+
this.render.emitLine("}");
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
getNexusType(
|
|
284
|
+
reference: PreparedTypeReference | undefined,
|
|
285
|
+
): Sourcelike | undefined {
|
|
286
|
+
if (!reference) {
|
|
287
|
+
return undefined;
|
|
288
|
+
} else if (reference.kind == "existing") {
|
|
289
|
+
// If there is a dot, need to take qualified package and get alias
|
|
290
|
+
const lastDot = reference.name.lastIndexOf(".");
|
|
291
|
+
if (lastDot > 0) {
|
|
292
|
+
const mport = reference.name.slice(0, lastDot);
|
|
293
|
+
return `${this.imports[mport]}.${reference.name.slice(lastDot + 1)}`;
|
|
294
|
+
}
|
|
295
|
+
return reference.name;
|
|
296
|
+
} else {
|
|
297
|
+
const type = this.render.topLevels.get(reference.name);
|
|
298
|
+
if (!type) {
|
|
299
|
+
throw new Error(`Unable to find type for ${reference.name}`);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// If the type is primitive, use the alias
|
|
303
|
+
if (type.isPrimitive()) {
|
|
304
|
+
return reference.name;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return this.render.goType(type);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import {
|
|
2
|
+
javaOptions,
|
|
3
|
+
JavaRenderer,
|
|
4
|
+
JavaTargetLanguage,
|
|
5
|
+
Namer,
|
|
6
|
+
Type,
|
|
7
|
+
type LanguageName,
|
|
8
|
+
type OptionValues,
|
|
9
|
+
type RenderContext,
|
|
10
|
+
type RendererOptions,
|
|
11
|
+
type Sourcelike,
|
|
12
|
+
} from "quicktype-core";
|
|
13
|
+
import { RenderAdapter, type RenderAccessible } from "./render-adapter.js";
|
|
14
|
+
import type { PreparedService, PreparedTypeReference } from "./generator.js";
|
|
15
|
+
import { splitDescription } from "./utility.js";
|
|
16
|
+
import { stringEscape } from "quicktype-core/dist/language/Java/utils.js";
|
|
17
|
+
|
|
18
|
+
// Change some defaults globally
|
|
19
|
+
javaOptions.packageName.definition.defaultValue = "com.example.nexusservices";
|
|
20
|
+
|
|
21
|
+
export class JavaLanguageWithNexus extends JavaTargetLanguage {
|
|
22
|
+
protected override get defaultIndentation(): string {
|
|
23
|
+
// We want two-space indent to be default for Java
|
|
24
|
+
return " ";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected override makeRenderer<Lang extends LanguageName = "java">(
|
|
28
|
+
renderContext: RenderContext,
|
|
29
|
+
untypedOptionValues: RendererOptions<Lang>,
|
|
30
|
+
): JavaRenderer {
|
|
31
|
+
const adapter = new JavaRenderAdapter(
|
|
32
|
+
super.makeRenderer(renderContext, untypedOptionValues),
|
|
33
|
+
untypedOptionValues,
|
|
34
|
+
);
|
|
35
|
+
return adapter.makeRenderer({
|
|
36
|
+
emitSourceStructure(original) {
|
|
37
|
+
adapter.emitServices();
|
|
38
|
+
original();
|
|
39
|
+
},
|
|
40
|
+
emitConverterClass(original) {
|
|
41
|
+
// No converter class wanted
|
|
42
|
+
},
|
|
43
|
+
forbiddenNamesForGlobalNamespace(original) {
|
|
44
|
+
const returnValue = original();
|
|
45
|
+
// We need to adjust some forbidden names to include Object base class items
|
|
46
|
+
returnValue.push(
|
|
47
|
+
"clone",
|
|
48
|
+
"equals",
|
|
49
|
+
"finalize",
|
|
50
|
+
"getClass",
|
|
51
|
+
"hashCode",
|
|
52
|
+
"notify",
|
|
53
|
+
"notifyAll",
|
|
54
|
+
"toString",
|
|
55
|
+
"wait",
|
|
56
|
+
);
|
|
57
|
+
return returnValue;
|
|
58
|
+
},
|
|
59
|
+
startFile(original, basename) {
|
|
60
|
+
// Prepend a the package name for Java
|
|
61
|
+
const prepend =
|
|
62
|
+
adapter.render._options.packageName.replaceAll(".", "/") + "/";
|
|
63
|
+
original([prepend, basename]);
|
|
64
|
+
// // Emit generated note
|
|
65
|
+
// adapter.render.emitLine("// Generated by nexus-rpc-gen. DO NOT EDIT!");
|
|
66
|
+
// adapter.render.ensureBlankLine();
|
|
67
|
+
},
|
|
68
|
+
emitPackageAndImports(original, imports) {
|
|
69
|
+
adapter.emitGeneratedComment();
|
|
70
|
+
original(imports);
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
type JavaRenderAccessible = JavaRenderer &
|
|
77
|
+
RenderAccessible & {
|
|
78
|
+
readonly _options: OptionValues<typeof javaOptions>;
|
|
79
|
+
emitBlock(line: Sourcelike, f: () => void): void;
|
|
80
|
+
emitConverterClass(): void;
|
|
81
|
+
emitFileHeader(fileName: Sourcelike, imports: string[]): void;
|
|
82
|
+
emitPackageAndImports(imports: string[]): void;
|
|
83
|
+
emitSourceStructure(): void;
|
|
84
|
+
finishFile(): void;
|
|
85
|
+
forbiddenNamesForGlobalNamespace(): string[];
|
|
86
|
+
javaImport(t: Type): string[];
|
|
87
|
+
javaType(reference: boolean, t: Type, withIssues?: boolean): Sourcelike;
|
|
88
|
+
makeNamedTypeNamer(): Namer;
|
|
89
|
+
namerForObjectProperty(): Namer;
|
|
90
|
+
startFile(basename: Sourcelike): void;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
class JavaRenderAdapter extends RenderAdapter<JavaRenderAccessible> {
|
|
94
|
+
emitServices() {
|
|
95
|
+
for (const [serviceName, serviceSchema] of Object.entries(
|
|
96
|
+
this.schema.services,
|
|
97
|
+
)) {
|
|
98
|
+
this.emitService(serviceName, serviceSchema);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
emitService(serviceName: string, serviceSchema: PreparedService) {
|
|
103
|
+
// Collect imports
|
|
104
|
+
const imports: string[] = ["io.nexusrpc.Operation", "io.nexusrpc.Service"];
|
|
105
|
+
for (const [_, op] of Object.entries(serviceSchema.operations)) {
|
|
106
|
+
if (op.input?.kind == "jsonSchema") {
|
|
107
|
+
const type = this.render.topLevels.get(op.input.name);
|
|
108
|
+
if (type) {
|
|
109
|
+
imports.push(...this.render.javaImport(type));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (op.output?.kind == "jsonSchema") {
|
|
113
|
+
const type = this.render.topLevels.get(op.output.name);
|
|
114
|
+
if (type) {
|
|
115
|
+
imports.push(...this.render.javaImport(type));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Create class
|
|
121
|
+
// TODO(cretz): Research addNameForTopLevel and such to prevent service name clash
|
|
122
|
+
const className = this.makeServiceTypeName(
|
|
123
|
+
this.render.makeNamedTypeNamer().nameStyle(serviceName),
|
|
124
|
+
);
|
|
125
|
+
const packagePrepend =
|
|
126
|
+
this.render._options.packageName.replaceAll(".", "/") + "/";
|
|
127
|
+
this.emitGeneratedComment();
|
|
128
|
+
this.render.emitFileHeader([packagePrepend, className], imports);
|
|
129
|
+
this.render.emitDescription(splitDescription(serviceSchema.description));
|
|
130
|
+
this.render.emitLine(
|
|
131
|
+
"@Service",
|
|
132
|
+
className == serviceName
|
|
133
|
+
? []
|
|
134
|
+
: ['(name="', stringEscape(serviceName), '")'],
|
|
135
|
+
);
|
|
136
|
+
const methodNamesInUse = {};
|
|
137
|
+
this.render.emitBlock(["public interface ", className], () => {
|
|
138
|
+
this.render.forEachWithBlankLines(
|
|
139
|
+
Object.entries(serviceSchema.operations),
|
|
140
|
+
"interposing",
|
|
141
|
+
(op, opName, pos) => {
|
|
142
|
+
this.render.emitDescription(splitDescription(op.description));
|
|
143
|
+
const methodName = this.makeOperationFunctionName(
|
|
144
|
+
this.render.namerForObjectProperty().nameStyle(opName),
|
|
145
|
+
methodNamesInUse,
|
|
146
|
+
);
|
|
147
|
+
this.render.emitLine(
|
|
148
|
+
"@Operation",
|
|
149
|
+
methodName == opName ? [] : ['(name="', stringEscape(opName), '")'],
|
|
150
|
+
);
|
|
151
|
+
const inType = this.getNexusType(op.input);
|
|
152
|
+
this.render.emitLine(
|
|
153
|
+
this.getNexusType(op.output) ?? "void",
|
|
154
|
+
" ",
|
|
155
|
+
methodName,
|
|
156
|
+
"(",
|
|
157
|
+
inType ? [inType, " input"] : [],
|
|
158
|
+
");",
|
|
159
|
+
);
|
|
160
|
+
},
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
this.render.finishFile();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
public emitGeneratedComment(): void {
|
|
167
|
+
this.render.emitLine("// Generated by nexus-rpc-gen. DO NOT EDIT!");
|
|
168
|
+
this.render.ensureBlankLine();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
protected emitPackageAndImports(imports: string[]): void {
|
|
172
|
+
this.emitGeneratedComment();
|
|
173
|
+
this.render.emitPackageAndImports(imports);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
getNexusType(
|
|
177
|
+
reference: PreparedTypeReference | undefined,
|
|
178
|
+
): Sourcelike | undefined {
|
|
179
|
+
if (!reference) {
|
|
180
|
+
return undefined;
|
|
181
|
+
} else if (reference.kind == "existing") {
|
|
182
|
+
return reference.name;
|
|
183
|
+
} else {
|
|
184
|
+
const type = this.render.topLevels.get(reference.name);
|
|
185
|
+
if (!type) {
|
|
186
|
+
throw new Error(`Unable to find type for ${reference.name}`);
|
|
187
|
+
}
|
|
188
|
+
return this.render.javaType(false, type);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|