Skip to content

Commit 7f6ab82

Browse files
committed
fix: import resolution for responses
1 parent 895ec27 commit 7f6ab82

11 files changed

Lines changed: 106 additions & 77 deletions

File tree

src/generation/client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import configuration from './utility/configuration.js';
1010
import { NotImplementedError } from './utility/errors.js';
1111
import lintAndCheckFiles from './utility/lintAndCheckFiles.js';
1212
import { default as rootLogger } from './utility/logger.js';
13-
import PathInfo, { resolveAbsolutePath } from './utility/PathInfo.js';
13+
import PathInfo, { FileInfo, resolveAbsolutePath } from './utility/PathInfo.js';
1414
import template from './utility/templater.js';
1515
import writeSourceFile from './utility/writeSourceFile.js';
1616

@@ -67,7 +67,7 @@ const processPaths = (
6767

6868
const buildClient = (
6969
functions: Awaited<ReturnType<typeof operationToFunction>>[],
70-
outFile: PathInfo,
70+
outFile: FileInfo,
7171
) => {
7272
functions.sort((a, b) => {
7373
if (a.operationName < b.operationName) {

src/generation/function.ts

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import buildResponseTypes, { ResponseTypes } from './response/responseTypes.js';
1515
import { GenerationError } from './utility/errors.js';
1616
import { ImportCollection, ImportSource, resolveImports } from './utility/importSource.js';
1717
import { default as rootLogger } from './utility/logger.js';
18-
import PathInfo from './utility/PathInfo.js';
18+
import PathInfo, { FileInfo } from './utility/PathInfo.js';
1919
import { sanitizeVariableName, uppercaseFirst } from './utility/sanitization.js';
2020
import template from './utility/templater.js';
2121

@@ -60,7 +60,7 @@ export type FunctionMetadata = {
6060
takesParameters: boolean;
6161
requiresParameters: boolean;
6262
importPath: string;
63-
systemPath: PathInfo;
63+
systemPath: FileInfo;
6464
};
6565
/**
6666
* Generate a function from an openapi operation
@@ -152,12 +152,6 @@ const operationToFunction = (
152152
);
153153
responseTypes = types;
154154

155-
for (const type of types) {
156-
if (type.imports) {
157-
imports.append(type.imports);
158-
}
159-
}
160-
161155
imports.append({
162156
file: {
163157
path: `${outDir.path}/${operationName}.responses.js`,
@@ -171,19 +165,14 @@ const operationToFunction = (
171165
},
172166
],
173167
});
174-
// eslint-disable-next-line @typescript-eslint/no-shadow
175-
const typeImports = types.flatMap(({ imports, typeName }) =>
176-
typeName && imports === undefined ? typeName : [],
177-
);
178-
for (const typeImport of typeImports) {
179-
imports.push(typeImport, `${outDir.path}/${operationName}.responses.js`, true);
180-
}
181-
// eslint-disable-next-line @typescript-eslint/no-shadow
182-
const validatorImports = types.flatMap(({ imports, validatorName }) =>
183-
validatorName && imports === undefined ? validatorName : [],
184-
);
185-
for (const validator of validatorImports) {
186-
imports.push(validator, `${outDir.path}/${operationName}.responses.js`);
168+
169+
for (const typeImport of types) {
170+
if (typeImport.responseEntry) {
171+
imports.append(typeImport.responseEntry.importMeta.type);
172+
if (typeImport.responseEntry.importMeta.validator) {
173+
imports.append(typeImport.responseEntry.importMeta.validator);
174+
}
175+
}
187176
}
188177
}
189178

src/generation/function/parameterType.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@ import { JSONSchema7Definition } from 'json-schema';
22
import Parameter from '../../openapi/Parameter.js';
33
import type RequestBody from '../../openapi/RequestBody.js';
44
import schemaToModel from '../model.js';
5-
import { ImportCollection, ImportSource, resolveImports } from '../utility/importSource.js';
5+
import {
6+
ImportCollection,
7+
ImportSource,
8+
resolveImports,
9+
toImportPath,
10+
} from '../utility/importSource.js';
611
import { default as rootLogger } from '../utility/logger.js';
7-
import PathInfo from '../utility/PathInfo.js';
12+
import { FileInfo } from '../utility/PathInfo.js';
813
import template from '../utility/templater.js';
914
import writeSourceFile from '../utility/writeSourceFile.js';
1015

@@ -123,7 +128,7 @@ const buildParameterType = (typeName: string, parameters: Parameter[]): TypeInfo
123128
};
124129

125130
const generateFunctionParameterType = (
126-
outFile: PathInfo,
131+
outFile: FileInfo,
127132
typeName: string,
128133
parameters: Parameter[] = [],
129134
requestBody?: RequestBody,
@@ -173,8 +178,7 @@ const generateFunctionParameterType = (
173178
contentType,
174179
import: {
175180
file: {
176-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
177-
path: `${outFile.path}/${outFile.filename!.replace('.ts', '.js')}`,
181+
path: toImportPath(outFile),
178182
internal: true,
179183
},
180184
entries: [

src/generation/function/returnStatement.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ const buildResponseReturn = (
1818
: {};
1919

2020
for (const [statusCode, response] of Object.entries(responses)) {
21+
if (response == null) {
22+
continue;
23+
}
24+
2125
let resolvedResponse;
2226
if ('$ref' in response) {
2327
const resolved = context.responses.lookup(response.$ref);
@@ -58,8 +62,10 @@ const buildResponseReturn = (
5862
template.lines(
5963
'return {',
6064
` status: ${statusCode},`,
61-
responseType.typeName && ` data: await response.blob() as ${responseType.typeName},`,
62-
responseType.validatorName && ` validator: ${responseType.validatorName},`,
65+
responseType.responseEntry?.typeName &&
66+
` data: await response.blob() as ${responseType.responseEntry?.typeName},`,
67+
responseType.responseEntry?.validatorName &&
68+
` validator: ${responseType.responseEntry?.validatorName},`,
6369
' validator: ',
6470
' response,',
6571
' request: requestMeta,',
@@ -71,8 +77,10 @@ const buildResponseReturn = (
7177
template.lines(
7278
'return {',
7379
` status: ${statusCode},`,
74-
responseType.typeName && ` data: await response.json() as ${responseType.typeName},`,
75-
responseType.validatorName && ` validator: ${responseType.validatorName},`,
80+
responseType.responseEntry?.typeName &&
81+
` data: await response.json() as ${responseType.responseEntry?.typeName},`,
82+
responseType.responseEntry?.validatorName &&
83+
` validator: ${responseType.responseEntry?.validatorName},`,
7684
' response,',
7785
' request: requestMeta,',
7886
'};',
@@ -90,10 +98,10 @@ const buildResponseReturn = (
9098
'if (response.status !== 0) {',
9199
' return {',
92100
" status: 'default',",
93-
defaultResponseTypename.typeName &&
94-
` data: await response.json() as ${defaultResponseTypename.typeName},`,
95-
defaultResponseTypename.validatorName &&
96-
` validator: ${defaultResponseTypename.validatorName},`,
101+
defaultResponseTypename.responseEntry?.typeName &&
102+
` data: await response.json() as ${defaultResponseTypename.responseEntry?.typeName},`,
103+
defaultResponseTypename.responseEntry?.validatorName &&
104+
` validator: ${defaultResponseTypename.responseEntry?.validatorName},`,
97105
' response,',
98106
' request: requestMeta,',
99107
' };',

src/generation/models.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import context, { SchemaEntry } from './utility/context.js';
55
import { MissingReferenceError } from './utility/errors.js';
66
import { ImportCollection, ImportSource, resolveImports } from './utility/importSource.js';
77
import { default as rootLogger } from './utility/logger.js';
8-
import PathInfo from './utility/PathInfo.js';
8+
import PathInfo, { FileInfo } from './utility/PathInfo.js';
99
import template from './utility/templater.js';
1010
import writeSourceFile from './utility/writeSourceFile.js';
1111

1212
const logger = rootLogger.child({ context: 'schema' });
1313

1414
const generateModelIndex = (location: PathInfo) => {
15-
const destination: PathInfo = {
15+
const destination: FileInfo = {
1616
...location,
1717
filename: 'index.ts',
1818
};
@@ -34,7 +34,7 @@ const generateModelIndex = (location: PathInfo) => {
3434

3535
type Schema = { schema: JSONSchema7; name: string; ref: string };
3636
const generateModels = (schemas: Schema[], outDir: PathInfo) => {
37-
const files: PathInfo[] = [];
37+
const files: FileInfo[] = [];
3838

3939
// TODO: build a tree instead, could then be parallelized
4040
// TODO: type assertion
@@ -75,7 +75,8 @@ const generateModels = (schemas: Schema[], outDir: PathInfo) => {
7575
} else {
7676
const sourceFile = {
7777
basePath: outDir.basePath,
78-
path: `${outDir.path}/${schemaModel.typeName}.ts`,
78+
path: outDir.path,
79+
filename: `${schemaModel.typeName}.ts`,
7980
};
8081
writeSourceFile(
8182
sourceFile,

src/generation/response/responseTypes.ts

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@ import Response from '../../openapi/Response.js';
22
import type Responses from '../../openapi/Responses.js';
33
import schemaToModel from '../model.js';
44
import typeboxImports from '../model/typeboxImports.js';
5-
import context from '../utility/context.js';
5+
import context, { ResponseEntry } from '../utility/context.js';
66
import { GenerationError } from '../utility/errors.js';
77
import {
88
ImportCollection,
99
ImportMetadata,
1010
ImportSource,
1111
resolveImports,
12+
toImportPath,
1213
} from '../utility/importSource.js';
13-
import PathInfo from '../utility/PathInfo.js';
14+
import { FileInfo } from '../utility/PathInfo.js';
1415
import template from '../utility/templater.js';
1516
import writeSourceFile from '../utility/writeSourceFile.js';
1617

@@ -73,14 +74,13 @@ export const buildResponseType = (
7374

7475
export type ResponseType = {
7576
responseCode: string;
76-
typeName?: string;
77-
validatorName?: string;
7877
schema?: string;
79-
imports?: ImportCollection;
78+
responseEntry?: ResponseEntry;
8079
};
8180
export type ResponseTypes = Array<ResponseType>;
8281

8382
const buildResponses = (
83+
outFile: FileInfo,
8484
responses: Responses,
8585
): {
8686
types: ResponseTypes;
@@ -92,21 +92,19 @@ const buildResponses = (
9292
const imports = new ImportCollection();
9393

9494
for (const [statusCode, response] of Object.entries(responses)) {
95+
if (response == null) {
96+
continue;
97+
}
98+
9599
if ('$ref' in response) {
96100
const r = context.responses.lookup(response.$ref);
97101
if (r === undefined) {
98102
throw new GenerationError(`Unresolved response reference ${response.$ref}`);
99103
}
100104
types.push({
101105
responseCode: statusCode,
102-
typeName: r.typeName,
103-
validatorName: r.validatorName,
104-
imports: new ImportCollection(r.importMeta.type, r.importMeta.validator),
106+
responseEntry: r,
105107
});
106-
imports.append(r.importMeta.type);
107-
if (r.importMeta.validator) {
108-
imports.append(r.importMeta.validator);
109-
}
110108
continue;
111109
}
112110

@@ -120,27 +118,49 @@ const buildResponses = (
120118
code.push(r.code);
121119
types.push({
122120
responseCode: statusCode,
123-
typeName: r.typeName,
124-
validatorName: r.validatorName,
125-
imports: r.imports,
121+
responseEntry: {
122+
typeName: r.typeName,
123+
validatorName: r.validatorName,
124+
importMeta: {
125+
type: {
126+
file: {
127+
path: toImportPath(outFile),
128+
internal: true,
129+
},
130+
entries: [{ item: r.typeName, typeOnly: true }],
131+
},
132+
...(r.validatorName != null && {
133+
validator: {
134+
file: {
135+
path: toImportPath(outFile),
136+
internal: true,
137+
},
138+
entries: [{ item: r.validatorName }],
139+
},
140+
}),
141+
},
142+
raw: response,
143+
},
126144
});
145+
if (r.imports) {
146+
imports.append(r.imports);
147+
}
127148
} else {
128149
types.push({
129150
responseCode: statusCode,
130-
typeName: r.typeName,
131-
validatorName: r.validatorName,
132-
imports: new ImportCollection(r.importMeta.type, r.importMeta.validator),
151+
responseEntry: {
152+
...r,
153+
raw: response,
154+
},
133155
});
134-
}
135-
if (r.type === 'import') {
136156
imports.append(r.importMeta.type);
137157
}
138158
}
139159
return { types, code, imports };
140160
};
141161

142162
const buildResponseTypes = (
143-
outFile: PathInfo,
163+
outFile: FileInfo,
144164
responseTypeName: string,
145165
responsesRaw: Responses,
146166
parameterType: { typename: string; imports: ImportSource } | null,
@@ -153,7 +173,7 @@ const buildResponseTypes = (
153173
if (parameterType !== null) {
154174
imports.append(parameterType.imports);
155175
}
156-
const responses = buildResponses(responsesRaw);
176+
const responses = buildResponses(outFile, responsesRaw);
157177
lines.push(...responses.code);
158178
imports.append(responses.imports);
159179

@@ -167,15 +187,15 @@ const buildResponseTypes = (
167187

168188
lines.push(
169189
template.lines(
170-
...responses.types.map(({ responseCode, typeName, validatorName }) =>
190+
...responses.types.map(({ responseCode, responseEntry }) =>
171191
template.concat(
172192
'| {',
173193
' response: Response;',
174194
' request: Request;',
175195
// "default" is a special openapi case that is not a number
176196
` status: ${responseCode === 'default' ? "'default'" : responseCode};`,
177-
typeName && ` data: ${typeName};`,
178-
validatorName && `validator: typeof ${validatorName};`,
197+
responseEntry?.typeName && ` data: ${responseEntry?.typeName};`,
198+
responseEntry?.validatorName && `validator: typeof ${responseEntry?.validatorName};`,
179199
' }',
180200
),
181201
),

src/generation/responses.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import fs from 'node:fs';
22
import type OpenApiSpec from '../openapi/index.js';
33
import refUnsupported from './function/helpers/refUnsupported.js';
44
import { buildResponseType } from './response/responseTypes.js';
5-
import PathInfo, { resolveAbsolutePath } from './utility/PathInfo.js';
5+
import PathInfo, { FileInfo, resolveAbsolutePath } from './utility/PathInfo.js';
66
import context from './utility/context.js';
77
import { ImportCollection, resolveImports } from './utility/importSource.js';
88
import { default as rootLogger } from './utility/logger.js';
@@ -11,8 +11,8 @@ import writeSourceFile from './utility/writeSourceFile.js';
1111

1212
const logger = rootLogger.child({ context: 'response' });
1313

14-
const generateResponseIndex = (location: PathInfo) => {
15-
const destination: PathInfo = {
14+
const generateResponseIndex = (location: FileInfo) => {
15+
const destination: FileInfo = {
1616
...location,
1717
filename: 'index.ts',
1818
};

src/generation/utility/PathInfo.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import path from 'node:path';
22

3-
type PathInfo = {
3+
export type PathInfo = {
44
basePath: string;
55
path: string;
6-
filename?: string;
6+
};
7+
export type FileInfo = {
8+
basePath: string;
9+
path: string;
10+
filename: string;
711
};
812

9-
export const resolveAbsolutePath = (where: PathInfo): string => {
10-
return where.filename
13+
export const resolveAbsolutePath = (where: PathInfo | FileInfo): string => {
14+
return 'filename' in where
1115
? path.resolve(where.basePath, where.path, where.filename)
1216
: path.resolve(where.basePath, where.path);
1317
};

0 commit comments

Comments
 (0)