Skip to content

Commit cf58307

Browse files
committed
fix: after rebase
1 parent 8dc1e2e commit cf58307

8 files changed

Lines changed: 95 additions & 57 deletions

File tree

packages/openapi-code-generator/src/core/input.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export type InputConfig = {
6666

6767
export class Input {
6868
constructor(
69-
private loader: OpenapiLoader,
69+
readonly loader: OpenapiLoader,
7070
readonly config: InputConfig,
7171
private readonly syntheticNameGenerator: SyntheticNameGenerator = defaultSyntheticNameGenerator,
7272
private readonly schemaNormalizer = new SchemaNormalizer(config),

packages/openapi-code-generator/src/core/schemas/tsconfig.schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export const tsconfigSchema = z.object({
3737
allowUnreachableCode: z.boolean(),
3838
rewriteRelativeImportExtensions: z.boolean(),
3939
verbatimModuleSyntax: z.boolean(),
40-
paths: z.record(z.array(z.string())),
40+
paths: z.record(z.string(), z.array(z.string())),
4141
})
4242
.partial()
4343
.optional()

packages/openapi-code-generator/src/generate.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,5 +93,6 @@ export async function generate(
9393
isEsmProject: config.tsIsEsmProject,
9494
allowAny: config.tsAllowAny,
9595
serverImplementationMethod: config.tsServerImplementationMethod,
96+
fsAdaptor,
9697
})
9798
}

packages/openapi-code-generator/src/templates.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export const templates = {
4242
language: "typescript",
4343
type: "server",
4444
run: generateTypescriptNextJS,
45+
syntheticNameGenerator: defaultSyntheticNameGenerator,
4546
},
4647
} satisfies {[key: string]: OpenapiGenerator}
4748

packages/openapi-code-generator/src/typescript/common/import-builder.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export function categorizeImportSource(source: string): ImportCategory {
8585
export type ImportBuilderConfig = {
8686
unit?: {filename: string} | undefined
8787
includeFileExtensions: boolean
88-
importAlias?: string
88+
importAlias?: string | undefined
8989
}
9090

9191
export class ImportBuilder {
@@ -225,17 +225,18 @@ export class ImportBuilder {
225225
}
226226
}
227227

228-
private normalizeFrom(from: string) {
228+
normalizeFrom(from: string) {
229229
if (!this.config.includeFileExtensions && from.endsWith(".ts")) {
230230
// biome-ignore lint/style/noParameterAssign: normalization
231231
from = from.substring(0, from.length - ".ts".length)
232232
}
233233

234234
// TODO: does this work on windows?
235235
if (this.config.unit && from.startsWith("./")) {
236-
237236
if (this.config.importAlias) {
238-
return this.config.importAlias + from.split(path.sep).slice(1).join(path.sep)
237+
return (
238+
this.config.importAlias + from.split(path.sep).slice(1).join(path.sep)
239+
)
239240
}
240241

241242
const unitDirname = path.dirname(this.config.unit.filename)

packages/openapi-code-generator/src/typescript/server/typescript-nextjs/typescript-nextjs-app-router-builder.ts

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {
22
type SourceFile,
33
StructureKind,
4-
VariableDeclarationKind,
54
ts,
5+
VariableDeclarationKind,
66
} from "ts-morph"
77
import type {Input} from "../../../core/input"
88
import type {IROperation} from "../../../core/openapi-types-normalized"
@@ -16,11 +16,28 @@ import {CompilationUnit, type ICompilable} from "../../common/compilation-units"
1616
import type {ImportBuilder} from "../../common/import-builder"
1717
import type {SchemaBuilder} from "../../common/schema-builders/schema-builder"
1818
import type {TypeBuilder} from "../../common/type-builder"
19-
import type {ServerSymbols} from "../abstract-router-builder"
20-
import {ServerOperationBuilder} from "../server-operation-builder"
19+
import {
20+
ServerOperationBuilder,
21+
type ServerSymbols,
22+
} from "../server-operation-builder"
23+
2124
import SyntaxKind = ts.SyntaxKind
2225

2326
export class TypescriptNextjsAppRouterBuilder implements ICompilable {
27+
protected readonly capabilities = {
28+
requestBody: {
29+
mediaTypes: [
30+
"application/json",
31+
"application/scim+json",
32+
"application/merge-patch+json",
33+
"application/x-www-form-urlencoded",
34+
"text/json",
35+
"text/plain",
36+
"text/x-markdown",
37+
],
38+
},
39+
}
40+
2441
constructor(
2542
private readonly filename: string,
2643
private readonly name: string,
@@ -40,10 +57,14 @@ export class TypescriptNextjsAppRouterBuilder implements ICompilable {
4057
this.input,
4158
this.types,
4259
this.schemaBuilder,
60+
{
61+
requestBody: {
62+
supportedMediaTypes: this.capabilities.requestBody.mediaTypes,
63+
},
64+
},
4365
)
4466

45-
const symbols = this.operationSymbols(builder.operationId)
46-
const params = builder.parameters(symbols)
67+
const params = builder.parameters()
4768

4869
const sourceFile = this.sourceFile
4970

@@ -135,10 +156,6 @@ export class TypescriptNextjsAppRouterBuilder implements ICompilable {
135156
implPropName: operationId,
136157
implTypeName: titleCase(operationId),
137158
responderName: `${titleCase(operationId)}Responder`,
138-
paramSchema: `${operationId}ParamSchema`,
139-
querySchema: `${operationId}QuerySchema`,
140-
requestBodySchema: `${operationId}BodySchema`,
141-
requestHeaderSchema: `${operationId}HeaderSchema`,
142159
responseBodyValidator: `${operationId}ResponseValidator`,
143160
}
144161
}
@@ -152,12 +169,15 @@ export class TypescriptNextjsAppRouterBuilder implements ICompilable {
152169
const imports = this.sourceFile.getImportDeclarations()
153170
const from = this.imports.normalizeFrom(
154171
`./${this.companionFilename}`,
155-
`./${this.filename}`,
172+
// todo
173+
// `./${this.filename}`,
156174
)
157175
// biome-ignore lint/complexity/noForEach: <explanation>
158176
imports
159177
.filter((it) => it.getModuleSpecifierValue().includes(from))
160-
.forEach((it) => it.remove())
178+
.forEach((it) => {
179+
it.remove()
180+
})
161181

162182
this.sourceFile.addImportDeclaration({
163183
namedImports: Array.from(this.httpMethodsUsed)
@@ -174,7 +194,9 @@ export class TypescriptNextjsAppRouterBuilder implements ICompilable {
174194
const name = it.getName()
175195
return isHttpMethod(name) && !this.httpMethodsUsed.has(name)
176196
})
177-
.forEach((it) => it.remove())
197+
.forEach((it) => {
198+
it.remove()
199+
})
178200

179201
return new CompilationUnit(
180202
this.filename,

packages/openapi-code-generator/src/typescript/server/typescript-nextjs/typescript-nextjs-router-builder.ts

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
import type {Input} from "../../../core/input"
22
import {isDefined, titleCase} from "../../../core/utils"
33
import type {ImportBuilder} from "../../common/import-builder"
4-
import {JoiBuilder} from "../../common/schema-builders/joi-schema-builder"
54
import type {SchemaBuilder} from "../../common/schema-builders/schema-builder"
6-
import {ZodBuilder} from "../../common/schema-builders/zod-schema-builder"
75
import type {TypeBuilder} from "../../common/type-builder"
86
import {constStatement} from "../../common/type-utils"
97
import {buildExport} from "../../common/typescript-common"
10-
import {
11-
AbstractRouterBuilder,
12-
type ServerSymbols,
13-
} from "../abstract-router-builder"
14-
import type {ServerOperationBuilder} from "../server-operation-builder"
8+
import {AbstractRouterBuilder} from "../abstract-router-builder"
9+
import type {
10+
ServerOperationBuilder,
11+
ServerSymbols,
12+
} from "../server-operation-builder"
1513

1614
export class TypescriptNextjsRouterBuilder extends AbstractRouterBuilder {
1715
private readonly operationTypes: {
@@ -51,38 +49,44 @@ export class TypescriptNextjsRouterBuilder extends AbstractRouterBuilder {
5149
.from("@nahkies/typescript-nextjs-runtime/errors")
5250
.add("OpenAPIRuntimeError", "RequestInputType")
5351

54-
if (this.schemaBuilder instanceof ZodBuilder) {
55-
this.imports
56-
.from("@nahkies/typescript-nextjs-runtime/zod")
57-
.add("parseRequestInput", "responseValidationFactory")
58-
} else if (this.schemaBuilder instanceof JoiBuilder) {
59-
this.imports
60-
.from("@nahkies/typescript-nextjs-runtime/joi")
61-
.add("parseRequestInput", "responseValidationFactory")
52+
const schemaBuilderType = this.schemaBuilder.type
53+
54+
switch (schemaBuilderType) {
55+
case "joi": {
56+
this.imports
57+
.from("@nahkies/typescript-nextjs-runtime/joi")
58+
.add("parseRequestInput", "responseValidationFactory")
59+
break
60+
}
61+
case "zod-v3":
62+
case "zod-v4": {
63+
this.imports
64+
.from("@nahkies/typescript-nextjs-runtime/zod")
65+
.add("parseRequestInput", "responseValidationFactory")
66+
break
67+
}
68+
default: {
69+
throw new Error(
70+
`unsupported schema builder type '${schemaBuilderType satisfies never}'`,
71+
)
72+
}
6273
}
6374
}
6475

6576
protected buildOperation(builder: ServerOperationBuilder): string {
6677
const statements: string[] = []
6778

6879
const symbols = this.operationSymbols(builder.operationId)
69-
const params = builder.parameters(symbols)
80+
const params = builder.parameters()
7081

7182
if (params.path.schema) {
72-
statements.push(constStatement(symbols.paramSchema, params.path.schema))
83+
statements.push(constStatement(params.path.name, params.path.schema))
7384
}
7485
if (params.query.schema) {
75-
statements.push(constStatement(symbols.querySchema, params.query.schema))
86+
statements.push(constStatement(params.query.name, params.query.schema))
7687
}
7788
if (params.header.schema) {
78-
statements.push(
79-
constStatement(symbols.requestHeaderSchema, params.header.schema),
80-
)
81-
}
82-
if (params.body.schema) {
83-
statements.push(
84-
constStatement(symbols.requestBodySchema, params.body.schema),
85-
)
89+
statements.push(constStatement(params.header.name, params.header.schema))
8690
}
8791

8892
const responseSchemas = builder.responseSchemas()
@@ -122,23 +126,24 @@ try {
122126
const input = {
123127
params: ${
124128
params.path.schema
125-
? `parseRequestInput(${symbols.paramSchema}, await params, RequestInputType.RouteParam)`
129+
? `parseRequestInput(${params.path.name}, await params, RequestInputType.RouteParam)`
126130
: "undefined"
127131
},
128132
// TODO: this swallows repeated parameters
129133
query: ${
130134
params.query.schema
131-
? `parseRequestInput(${symbols.querySchema}, Object.fromEntries(request.nextUrl.searchParams.entries()), RequestInputType.QueryString)`
135+
? `parseRequestInput(${params.query.name}, Object.fromEntries(request.nextUrl.searchParams.entries()), RequestInputType.QueryString)`
132136
: "undefined"
133137
},
138+
${params.body.schema && !params.body.isSupported ? `// todo: request bodies with content-type '${params.body.contentType}' not yet supported\n` : ""}
134139
body: ${
135140
params.body.schema
136-
? `parseRequestInput(${symbols.requestBodySchema}, await request.json(), RequestInputType.RequestBody)`
141+
? `parseRequestInput(${params.body.schema}, await request.json(), RequestInputType.RequestBody)${!params.body.isSupported ? " as never" : ""}`
137142
: "undefined"
138143
},
139144
headers: ${
140145
params.header.schema
141-
? `parseRequestInput(${symbols.requestHeaderSchema}, Reflect.get(request, "headers"), RequestInputType.RequestHeader)`
146+
? `parseRequestInput(${params.header.name}, Reflect.get(request, "headers"), RequestInputType.RequestHeader)`
142147
: "undefined"
143148
}
144149
}
@@ -172,10 +177,6 @@ try {
172177
implPropName: operationId,
173178
implTypeName: titleCase(operationId),
174179
responderName: `${titleCase(operationId)}Responder`,
175-
paramSchema: `${operationId}ParamSchema`,
176-
querySchema: `${operationId}QuerySchema`,
177-
requestBodySchema: `${operationId}BodySchema`,
178-
requestHeaderSchema: `${operationId}HeaderSchema`,
179180
responseBodyValidator: `${operationId}ResponseValidator`,
180181
}
181182
}

packages/openapi-code-generator/src/typescript/server/typescript-nextjs/typescript-nextjs.generator.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ export async function generateTypescriptNextJS(
3232
config.compilerOptions,
3333
)
3434

35+
const importBuilderConfig = {
36+
includeFileExtensions: config.isEsmProject,
37+
importAlias,
38+
}
39+
40+
const schemaBuilderImports = new ImportBuilder(importBuilderConfig)
41+
3542
// biome-ignore lint/complexity/useLiteralKeys: <explanation>
3643
const subDirectory = process.env["OPENAPI_INTEGRATION_TESTS"]
3744
? path.basename(config.input.loader.entryPointKey)
@@ -57,6 +64,8 @@ export async function generateTypescriptNextJS(
5764
input,
5865
config.schemaBuilder,
5966
{allowAny},
67+
schemaBuilderImports,
68+
rootTypeBuilder,
6069
)
6170

6271
const project = new Project({useInMemoryFileSystem: true})
@@ -69,7 +78,10 @@ export async function generateTypescriptNextJS(
6978
routeToNextJSFilepath(group.name),
7079
)
7180

72-
const imports = new ImportBuilder({filename}, importAlias)
81+
const imports = new ImportBuilder({
82+
...importBuilderConfig,
83+
unit: {filename},
84+
})
7385

7486
const routerBuilder = new TypescriptNextjsRouterBuilder(
7587
filename,
@@ -117,10 +129,10 @@ export async function generateTypescriptNextJS(
117129
).flat()
118130

119131
const clientOutputPath = [generatedDirectory, "client.ts"].join(path.sep)
120-
const clientImportBuilder = new ImportBuilder(
121-
{filename: clientOutputPath},
122-
importAlias,
123-
)
132+
const clientImportBuilder = new ImportBuilder({
133+
...importBuilderConfig,
134+
unit: {filename: clientOutputPath},
135+
})
124136

125137
const fetchClientBuilder = new TypescriptFetchClientBuilder(
126138
clientOutputPath,

0 commit comments

Comments
 (0)