ng-openapi 0.1.17 → 0.1.18

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 (4) hide show
  1. package/cli.cjs +64 -208
  2. package/index.d.ts +1 -3
  3. package/index.js +69 -215
  4. package/package.json +1 -1
package/cli.cjs CHANGED
@@ -44,7 +44,7 @@ var fs4 = __toESM(require("fs"));
44
44
  var path12 = __toESM(require("path"));
45
45
 
46
46
  // package.json
47
- var version = "0.1.16";
47
+ var version = "0.1.17";
48
48
 
49
49
  // src/lib/core/generator.ts
50
50
  var import_ts_morph7 = require("ts-morph");
@@ -107,7 +107,7 @@ function getTypeScriptType(schemaOrType, config, formatOrNullable, isNullable, c
107
107
  case "boolean":
108
108
  return nullableType("boolean", nullable);
109
109
  case "object":
110
- return nullableType(context === "type" ? "Record<string, unknown>" : "any", nullable);
110
+ return nullableType(context === "type" ? "Record<string, any>" : "any", nullable);
111
111
  case "null":
112
112
  return "null";
113
113
  default:
@@ -124,78 +124,6 @@ function escapeString(str) {
124
124
  }
125
125
  __name(escapeString, "escapeString");
126
126
 
127
- // ../shared/src/utils/functions/collect-used-types.ts
128
- function collectUsedTypes(operations) {
129
- const usedTypes = /* @__PURE__ */ new Set();
130
- usedTypes.add("RequestOptions");
131
- operations.forEach((operation) => {
132
- operation.parameters?.forEach((param) => {
133
- collectTypesFromSchema(param.schema || param, usedTypes);
134
- });
135
- if (operation.requestBody) {
136
- collectTypesFromRequestBody(operation.requestBody, usedTypes);
137
- }
138
- if (operation.responses) {
139
- Object.values(operation.responses).forEach((response) => {
140
- collectTypesFromResponse(response, usedTypes);
141
- });
142
- }
143
- });
144
- return usedTypes;
145
- }
146
- __name(collectUsedTypes, "collectUsedTypes");
147
- function collectTypesFromSchema(schema, usedTypes) {
148
- if (!schema) return;
149
- if (schema.$ref) {
150
- const refName = schema.$ref.split("/").pop();
151
- if (refName) {
152
- usedTypes.add(pascalCase(refName));
153
- }
154
- }
155
- if (schema.type === "array" && schema.items) {
156
- collectTypesFromSchema(schema.items, usedTypes);
157
- }
158
- if (schema.type === "object" && schema.properties) {
159
- Object.values(schema.properties).forEach((prop) => {
160
- collectTypesFromSchema(prop, usedTypes);
161
- });
162
- }
163
- if (schema.allOf) {
164
- schema.allOf.forEach((subSchema) => {
165
- collectTypesFromSchema(subSchema, usedTypes);
166
- });
167
- }
168
- if (schema.oneOf) {
169
- schema.oneOf.forEach((subSchema) => {
170
- collectTypesFromSchema(subSchema, usedTypes);
171
- });
172
- }
173
- if (schema.anyOf) {
174
- schema.anyOf.forEach((subSchema) => {
175
- collectTypesFromSchema(subSchema, usedTypes);
176
- });
177
- }
178
- }
179
- __name(collectTypesFromSchema, "collectTypesFromSchema");
180
- function collectTypesFromRequestBody(requestBody, usedTypes) {
181
- const content = requestBody.content || {};
182
- Object.values(content).forEach((mediaType) => {
183
- if (mediaType.schema) {
184
- collectTypesFromSchema(mediaType.schema, usedTypes);
185
- }
186
- });
187
- }
188
- __name(collectTypesFromRequestBody, "collectTypesFromRequestBody");
189
- function collectTypesFromResponse(response, usedTypes) {
190
- const content = response.content || {};
191
- Object.values(content).forEach((mediaType) => {
192
- if (mediaType.schema) {
193
- collectTypesFromSchema(mediaType.schema, usedTypes);
194
- }
195
- });
196
- }
197
- __name(collectTypesFromResponse, "collectTypesFromResponse");
198
-
199
127
  // ../shared/src/utils/functions/token-names.ts
200
128
  function getClientContextTokenName(clientName = "default") {
201
129
  const clientSuffix = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
@@ -1191,18 +1119,6 @@ var FileDownloadGenerator = class {
1191
1119
  const sourceFile = this.project.createSourceFile(filePath, "", {
1192
1120
  overwrite: true
1193
1121
  });
1194
- sourceFile.addImportDeclaration({
1195
- namedImports: [
1196
- "Observable"
1197
- ],
1198
- moduleSpecifier: "rxjs"
1199
- });
1200
- sourceFile.addImportDeclaration({
1201
- namedImports: [
1202
- "tap"
1203
- ],
1204
- moduleSpecifier: "rxjs/operators"
1205
- });
1206
1122
  sourceFile.addFunction({
1207
1123
  name: "downloadFile",
1208
1124
  isExported: true,
@@ -1312,7 +1228,7 @@ var FileDownloadGenerator = class {
1312
1228
 
1313
1229
  return fallbackFilename;`
1314
1230
  });
1315
- sourceFile.formatText();
1231
+ sourceFile.fixMissingImports().organizeImports().formatText();
1316
1232
  sourceFile.saveSync();
1317
1233
  }
1318
1234
  };
@@ -1334,34 +1250,6 @@ var DateTransformerGenerator = class {
1334
1250
  const sourceFile = this.project.createSourceFile(filePath, "", {
1335
1251
  overwrite: true
1336
1252
  });
1337
- sourceFile.addImportDeclaration({
1338
- namedImports: [
1339
- "HttpInterceptor",
1340
- "HttpRequest",
1341
- "HttpHandler",
1342
- "HttpEvent",
1343
- "HttpResponse"
1344
- ],
1345
- moduleSpecifier: "@angular/common/http"
1346
- });
1347
- sourceFile.addImportDeclaration({
1348
- namedImports: [
1349
- "Injectable"
1350
- ],
1351
- moduleSpecifier: "@angular/core"
1352
- });
1353
- sourceFile.addImportDeclaration({
1354
- namedImports: [
1355
- "Observable"
1356
- ],
1357
- moduleSpecifier: "rxjs"
1358
- });
1359
- sourceFile.addImportDeclaration({
1360
- namedImports: [
1361
- "map"
1362
- ],
1363
- moduleSpecifier: "rxjs/operators"
1364
- });
1365
1253
  sourceFile.addVariableStatement({
1366
1254
  isExported: true,
1367
1255
  declarationKind: import_ts_morph3.VariableDeclarationKind.Const,
@@ -1448,7 +1336,7 @@ var DateTransformerGenerator = class {
1448
1336
  }
1449
1337
  ]
1450
1338
  });
1451
- sourceFile.formatText();
1339
+ sourceFile.fixMissingImports().organizeImports().formatText();
1452
1340
  sourceFile.saveSync();
1453
1341
  }
1454
1342
  };
@@ -1733,33 +1621,9 @@ var BaseInterceptorGenerator = class {
1733
1621
  sourceFile.addImportDeclarations([
1734
1622
  {
1735
1623
  namedImports: [
1736
- "HttpEvent",
1737
- "HttpHandler",
1738
- "HttpInterceptor",
1739
- "HttpRequest",
1740
- "HttpContextToken"
1741
- ],
1742
- moduleSpecifier: "@angular/common/http"
1743
- },
1744
- {
1745
- namedImports: [
1746
- "inject",
1747
1624
  "Injectable"
1748
1625
  ],
1749
1626
  moduleSpecifier: "@angular/core"
1750
- },
1751
- {
1752
- namedImports: [
1753
- "Observable"
1754
- ],
1755
- moduleSpecifier: "rxjs"
1756
- },
1757
- {
1758
- namedImports: [
1759
- interceptorsTokenName,
1760
- clientContextTokenName
1761
- ],
1762
- moduleSpecifier: "../tokens"
1763
1627
  }
1764
1628
  ]);
1765
1629
  sourceFile.addClass({
@@ -1825,7 +1689,7 @@ var BaseInterceptorGenerator = class {
1825
1689
  }
1826
1690
  ]
1827
1691
  });
1828
- sourceFile.formatText();
1692
+ sourceFile.fixMissingImports().organizeImports().formatText();
1829
1693
  sourceFile.saveSync();
1830
1694
  }
1831
1695
  getInterceptorsTokenName() {
@@ -2061,8 +1925,10 @@ var ServiceMethodBodyGenerator = class {
2061
1925
  __name(this, "ServiceMethodBodyGenerator");
2062
1926
  }
2063
1927
  config;
2064
- constructor(config) {
1928
+ parser;
1929
+ constructor(config, parser) {
2065
1930
  this.config = config;
1931
+ this.parser = parser;
2066
1932
  }
2067
1933
  generateMethodBody(operation) {
2068
1934
  const context = this.createGenerationContext(operation);
@@ -2083,7 +1949,12 @@ var ServiceMethodBodyGenerator = class {
2083
1949
  if (!this.isMultipartFormData(operation)) {
2084
1950
  return [];
2085
1951
  }
2086
- const properties = operation.requestBody?.content?.["multipart/form-data"]?.schema?.properties || {};
1952
+ const schema = operation.requestBody?.content?.["multipart/form-data"].schema;
1953
+ let resolvedSchema = schema;
1954
+ if (schema?.$ref) {
1955
+ resolvedSchema = this.parser.resolveReference(schema.$ref);
1956
+ }
1957
+ const properties = resolvedSchema?.properties || {};
2087
1958
  return Object.keys(properties);
2088
1959
  }
2089
1960
  createGenerationContext(operation) {
@@ -2152,13 +2023,33 @@ if (!headers.has('Content-Type')) {
2152
2023
  if (!context.isMultipart || context.formDataFields.length === 0) {
2153
2024
  return "";
2154
2025
  }
2026
+ const schema = operation.requestBody?.content?.["multipart/form-data"].schema;
2027
+ let resolvedSchema = schema;
2028
+ if (schema?.$ref) {
2029
+ resolvedSchema = this.parser.resolveReference(schema.$ref);
2030
+ }
2031
+ const properties = resolvedSchema?.properties || {};
2155
2032
  const formDataAppends = context.formDataFields.map((field) => {
2156
- const fieldSchema = operation.requestBody?.content?.["multipart/form-data"]?.schema?.properties?.[field];
2033
+ const fieldSchema = properties[field];
2157
2034
  const isFile = fieldSchema?.type === "string" && fieldSchema?.format === "binary";
2158
- const valueExpression = isFile ? field : `String(${field})`;
2159
- return `if (${field} !== undefined) {
2160
- formData.append('${field}', ${valueExpression});
2161
- }`;
2035
+ const isArray = fieldSchema?.type === "array";
2036
+ if (isArray) {
2037
+ const itemSchema = Array.isArray(fieldSchema.items) ? fieldSchema.items[0] : fieldSchema.items;
2038
+ const isFileArray = itemSchema?.type === "string" && itemSchema?.format === "binary";
2039
+ const valueExpression = isFileArray ? "item" : "String(item)";
2040
+ return `if (${field} !== undefined && Array.isArray(${field})) {
2041
+ ${field}.forEach((item) => {
2042
+ if (item !== undefined && item !== null) {
2043
+ formData.append('${field}', ${valueExpression});
2044
+ }
2045
+ });
2046
+ }`;
2047
+ } else {
2048
+ const valueExpression = isFile ? field : `String(${field})`;
2049
+ return `if (${field} !== undefined) {
2050
+ formData.append('${field}', ${valueExpression});
2051
+ }`;
2052
+ }
2162
2053
  }).join("\n");
2163
2054
  return `
2164
2055
  const formData = new FormData();
@@ -2235,8 +2126,10 @@ var ServiceMethodParamsGenerator = class {
2235
2126
  __name(this, "ServiceMethodParamsGenerator");
2236
2127
  }
2237
2128
  config;
2238
- constructor(config) {
2129
+ parser;
2130
+ constructor(config, parser) {
2239
2131
  this.config = config;
2132
+ this.parser = parser;
2240
2133
  }
2241
2134
  generateMethodParameters(operation) {
2242
2135
  const params = this.generateApiParameters(operation);
@@ -2266,11 +2159,16 @@ var ServiceMethodParamsGenerator = class {
2266
2159
  });
2267
2160
  });
2268
2161
  if (operation.requestBody && operation.requestBody?.content?.["multipart/form-data"]) {
2269
- Object.entries(operation.requestBody?.content?.["multipart/form-data"].schema?.properties ?? {}).forEach(([key, value]) => {
2162
+ const schema = operation.requestBody.content["multipart/form-data"].schema;
2163
+ let resolvedSchema = schema;
2164
+ if (schema?.$ref) {
2165
+ resolvedSchema = this.parser.resolveReference(schema.$ref);
2166
+ }
2167
+ Object.entries(resolvedSchema?.properties ?? {}).forEach(([key, value]) => {
2270
2168
  params.push({
2271
2169
  name: key,
2272
2170
  type: getTypeScriptType(value, this.config, value.nullable),
2273
- hasQuestionToken: !value.required
2171
+ hasQuestionToken: !resolvedSchema?.required?.includes(key)
2274
2172
  });
2275
2173
  });
2276
2174
  }
@@ -2336,9 +2234,9 @@ var ServiceMethodOverloadsGenerator = class {
2336
2234
  config;
2337
2235
  paramsGenerator;
2338
2236
  responseDataType = "any";
2339
- constructor(config) {
2237
+ constructor(config, parser) {
2340
2238
  this.config = config;
2341
- this.paramsGenerator = new ServiceMethodParamsGenerator(config);
2239
+ this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
2342
2240
  }
2343
2241
  generateMethodOverloads(operation) {
2344
2242
  const observeTypes = [
@@ -2452,11 +2350,11 @@ var ServiceMethodGenerator = class {
2452
2350
  bodyGenerator;
2453
2351
  overloadsGenerator;
2454
2352
  paramsGenerator;
2455
- constructor(config) {
2353
+ constructor(config, parser) {
2456
2354
  this.config = config;
2457
- this.bodyGenerator = new ServiceMethodBodyGenerator(config);
2458
- this.overloadsGenerator = new ServiceMethodOverloadsGenerator(config);
2459
- this.paramsGenerator = new ServiceMethodParamsGenerator(config);
2355
+ this.bodyGenerator = new ServiceMethodBodyGenerator(config, parser);
2356
+ this.overloadsGenerator = new ServiceMethodOverloadsGenerator(config, parser);
2357
+ this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
2460
2358
  }
2461
2359
  addServiceMethod(serviceClass, operation) {
2462
2360
  const methodName = this.generateMethodName(operation);
@@ -2520,7 +2418,7 @@ var ServiceGenerator = class {
2520
2418
  const versionInfo = this.parser.getSpecVersion();
2521
2419
  throw new Error(`Invalid or unsupported specification format. Expected OpenAPI 3.x or Swagger 2.x. ${versionInfo ? `Found: ${versionInfo.type} ${versionInfo.version}` : "No version info found"}`);
2522
2420
  }
2523
- this.methodGenerator = new ServiceMethodGenerator(config);
2421
+ this.methodGenerator = new ServiceMethodGenerator(config, parser);
2524
2422
  }
2525
2423
  async generate(outputRoot) {
2526
2424
  const outputDir = path9.join(outputRoot, "services");
@@ -2558,62 +2456,20 @@ var ServiceGenerator = class {
2558
2456
  const sourceFile = this.project.createSourceFile(filePath, "", {
2559
2457
  overwrite: true
2560
2458
  });
2561
- const usedTypes = collectUsedTypes(operations);
2562
- this.addImports(sourceFile, usedTypes);
2459
+ this.addImports(sourceFile);
2563
2460
  this.addServiceClass(sourceFile, controllerName, operations);
2564
- sourceFile.formatText();
2461
+ sourceFile.fixMissingImports().organizeImports().fixUnusedIdentifiers().formatText();
2565
2462
  sourceFile.saveSync();
2566
2463
  }
2567
- addImports(sourceFile, usedTypes) {
2568
- const basePathTokenName = getBasePathTokenName(this.config.clientName);
2569
- const clientContextTokenName = getClientContextTokenName(this.config.clientName);
2464
+ addImports(sourceFile) {
2570
2465
  sourceFile.addImportDeclarations([
2571
2466
  {
2572
2467
  namedImports: [
2573
- "Injectable",
2574
- "inject"
2468
+ "Injectable"
2575
2469
  ],
2576
2470
  moduleSpecifier: "@angular/core"
2577
- },
2578
- {
2579
- namedImports: [
2580
- "HttpClient",
2581
- "HttpParams",
2582
- "HttpHeaders",
2583
- "HttpContext",
2584
- "HttpResponse",
2585
- "HttpEvent",
2586
- "HttpContextToken"
2587
- ],
2588
- moduleSpecifier: "@angular/common/http"
2589
- },
2590
- {
2591
- namedImports: [
2592
- "Observable",
2593
- "map"
2594
- ],
2595
- moduleSpecifier: "rxjs"
2596
- },
2597
- {
2598
- namedImports: [
2599
- basePathTokenName,
2600
- clientContextTokenName
2601
- ],
2602
- moduleSpecifier: "../tokens"
2603
- },
2604
- {
2605
- namedImports: [
2606
- "HttpParamsBuilder"
2607
- ],
2608
- moduleSpecifier: "../index"
2609
2471
  }
2610
2472
  ]);
2611
- if (usedTypes.size > 0) {
2612
- sourceFile.addImportDeclaration({
2613
- namedImports: Array.from(usedTypes).sort(),
2614
- moduleSpecifier: "../models"
2615
- });
2616
- }
2617
2473
  }
2618
2474
  addServiceClass(sourceFile, controllerName, operations) {
2619
2475
  const className = `${controllerName}Service`;
@@ -2765,15 +2621,15 @@ async function generateFromConfig(config) {
2765
2621
  fileDownloadHelper.generate(outputPath);
2766
2622
  const httpParamsBuilderGenerator = new HttpParamsBuilderGenerator(project);
2767
2623
  httpParamsBuilderGenerator.generate(outputPath);
2624
+ const providerGenerator = new ProviderGenerator(project, config);
2625
+ providerGenerator.generate(outputPath);
2626
+ const baseInterceptorGenerator = new BaseInterceptorGenerator(project, config.clientName);
2627
+ baseInterceptorGenerator.generate(outputPath);
2768
2628
  const serviceGenerator = new ServiceGenerator(swaggerParser, project, config);
2769
2629
  await serviceGenerator.generate(outputPath);
2770
2630
  const indexGenerator = new ServiceIndexGenerator(project);
2771
2631
  indexGenerator.generateIndex(outputPath);
2772
2632
  console.log(`\u2705 Angular services generated`);
2773
- const providerGenerator = new ProviderGenerator(project, config);
2774
- providerGenerator.generate(outputPath);
2775
- const baseInterceptorGenerator = new BaseInterceptorGenerator(project, config.clientName);
2776
- baseInterceptorGenerator.generate(outputPath);
2777
2633
  }
2778
2634
  if (config.plugins?.length) {
2779
2635
  for (const plugin of config.plugins) {
package/index.d.ts CHANGED
@@ -237,8 +237,6 @@ declare function escapeString(str: string): string;
237
237
 
238
238
  type placeHolder = {};
239
239
 
240
- declare function collectUsedTypes(operations: PathInfo[]): Set<string>;
241
-
242
240
  declare function getClientContextTokenName(clientName?: string): string;
243
241
  declare function getBasePathTokenName(clientName?: string): string;
244
242
 
@@ -278,4 +276,4 @@ declare function validateInput(inputPath: string): void;
278
276
  */
279
277
  declare function generateFromConfig(config: GeneratorConfig): Promise<void>;
280
278
 
281
- export { BASE_INTERCEPTOR_HEADER_COMMENT, type EnumValueObject, type GeneratorConfig, type GetMethodGenerationContext, HTTP_RESOURCE_GENERATOR_HEADER_COMMENT, type IPluginGenerator, type IPluginGeneratorClass, MAIN_INDEX_GENERATOR_HEADER_COMMENT, type MethodGenerationContext, type NgOpenapiClientConfig, PROVIDER_GENERATOR_HEADER_COMMENT, type Parameter, type PathInfo, type RequestBody, SERVICE_GENERATOR_HEADER_COMMENT, SERVICE_INDEX_GENERATOR_HEADER_COMMENT, type SwaggerDefinition, SwaggerParser, type SwaggerResponse, type SwaggerSpec, TYPE_GENERATOR_HEADER_COMMENT, type TypeSchema, camelCase, collectUsedTypes, escapeString, extractPaths, generateFromConfig, generateParseRequestTypeParams, getBasePathTokenName, getClientContextTokenName, getRequestBodyType, getResponseType, getResponseTypeFromResponse, getTypeScriptType, hasDuplicateFunctionNames, inferResponseTypeFromContentType, isDataTypeInterface, isPrimitiveType, kebabCase, nullableType, pascalCase, type placeHolder, screamingSnakeCase, validateInput };
279
+ export { BASE_INTERCEPTOR_HEADER_COMMENT, type EnumValueObject, type GeneratorConfig, type GetMethodGenerationContext, HTTP_RESOURCE_GENERATOR_HEADER_COMMENT, type IPluginGenerator, type IPluginGeneratorClass, MAIN_INDEX_GENERATOR_HEADER_COMMENT, type MethodGenerationContext, type NgOpenapiClientConfig, PROVIDER_GENERATOR_HEADER_COMMENT, type Parameter, type PathInfo, type RequestBody, SERVICE_GENERATOR_HEADER_COMMENT, SERVICE_INDEX_GENERATOR_HEADER_COMMENT, type SwaggerDefinition, SwaggerParser, type SwaggerResponse, type SwaggerSpec, TYPE_GENERATOR_HEADER_COMMENT, type TypeSchema, camelCase, escapeString, extractPaths, generateFromConfig, generateParseRequestTypeParams, getBasePathTokenName, getClientContextTokenName, getRequestBodyType, getResponseType, getResponseTypeFromResponse, getTypeScriptType, hasDuplicateFunctionNames, inferResponseTypeFromContentType, isDataTypeInterface, isPrimitiveType, kebabCase, nullableType, pascalCase, type placeHolder, screamingSnakeCase, validateInput };
package/index.js CHANGED
@@ -82,7 +82,6 @@ __export(index_exports, {
82
82
  SwaggerParser: () => SwaggerParser,
83
83
  TYPE_GENERATOR_HEADER_COMMENT: () => TYPE_GENERATOR_HEADER_COMMENT,
84
84
  camelCase: () => camelCase,
85
- collectUsedTypes: () => collectUsedTypes,
86
85
  escapeString: () => escapeString,
87
86
  extractPaths: () => extractPaths,
88
87
  generateFromConfig: () => generateFromConfig,
@@ -174,7 +173,7 @@ function getTypeScriptType(schemaOrType, config, formatOrNullable, isNullable, c
174
173
  case "boolean":
175
174
  return nullableType("boolean", nullable);
176
175
  case "object":
177
- return nullableType(context === "type" ? "Record<string, unknown>" : "any", nullable);
176
+ return nullableType(context === "type" ? "Record<string, any>" : "any", nullable);
178
177
  case "null":
179
178
  return "null";
180
179
  default:
@@ -191,79 +190,6 @@ function escapeString(str) {
191
190
  }
192
191
  __name(escapeString, "escapeString");
193
192
 
194
- // ../shared/src/utils/functions/collect-used-types.ts
195
- function collectUsedTypes(operations) {
196
- const usedTypes = /* @__PURE__ */ new Set();
197
- usedTypes.add("RequestOptions");
198
- operations.forEach((operation) => {
199
- var _a;
200
- (_a = operation.parameters) == null ? void 0 : _a.forEach((param) => {
201
- collectTypesFromSchema(param.schema || param, usedTypes);
202
- });
203
- if (operation.requestBody) {
204
- collectTypesFromRequestBody(operation.requestBody, usedTypes);
205
- }
206
- if (operation.responses) {
207
- Object.values(operation.responses).forEach((response) => {
208
- collectTypesFromResponse(response, usedTypes);
209
- });
210
- }
211
- });
212
- return usedTypes;
213
- }
214
- __name(collectUsedTypes, "collectUsedTypes");
215
- function collectTypesFromSchema(schema, usedTypes) {
216
- if (!schema) return;
217
- if (schema.$ref) {
218
- const refName = schema.$ref.split("/").pop();
219
- if (refName) {
220
- usedTypes.add(pascalCase(refName));
221
- }
222
- }
223
- if (schema.type === "array" && schema.items) {
224
- collectTypesFromSchema(schema.items, usedTypes);
225
- }
226
- if (schema.type === "object" && schema.properties) {
227
- Object.values(schema.properties).forEach((prop) => {
228
- collectTypesFromSchema(prop, usedTypes);
229
- });
230
- }
231
- if (schema.allOf) {
232
- schema.allOf.forEach((subSchema) => {
233
- collectTypesFromSchema(subSchema, usedTypes);
234
- });
235
- }
236
- if (schema.oneOf) {
237
- schema.oneOf.forEach((subSchema) => {
238
- collectTypesFromSchema(subSchema, usedTypes);
239
- });
240
- }
241
- if (schema.anyOf) {
242
- schema.anyOf.forEach((subSchema) => {
243
- collectTypesFromSchema(subSchema, usedTypes);
244
- });
245
- }
246
- }
247
- __name(collectTypesFromSchema, "collectTypesFromSchema");
248
- function collectTypesFromRequestBody(requestBody, usedTypes) {
249
- const content = requestBody.content || {};
250
- Object.values(content).forEach((mediaType) => {
251
- if (mediaType.schema) {
252
- collectTypesFromSchema(mediaType.schema, usedTypes);
253
- }
254
- });
255
- }
256
- __name(collectTypesFromRequestBody, "collectTypesFromRequestBody");
257
- function collectTypesFromResponse(response, usedTypes) {
258
- const content = response.content || {};
259
- Object.values(content).forEach((mediaType) => {
260
- if (mediaType.schema) {
261
- collectTypesFromSchema(mediaType.schema, usedTypes);
262
- }
263
- });
264
- }
265
- __name(collectTypesFromResponse, "collectTypesFromResponse");
266
-
267
193
  // ../shared/src/utils/functions/token-names.ts
268
194
  function getClientContextTokenName(clientName = "default") {
269
195
  const clientSuffix = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
@@ -1308,18 +1234,6 @@ var _FileDownloadGenerator = class _FileDownloadGenerator {
1308
1234
  const sourceFile = this.project.createSourceFile(filePath, "", {
1309
1235
  overwrite: true
1310
1236
  });
1311
- sourceFile.addImportDeclaration({
1312
- namedImports: [
1313
- "Observable"
1314
- ],
1315
- moduleSpecifier: "rxjs"
1316
- });
1317
- sourceFile.addImportDeclaration({
1318
- namedImports: [
1319
- "tap"
1320
- ],
1321
- moduleSpecifier: "rxjs/operators"
1322
- });
1323
1237
  sourceFile.addFunction({
1324
1238
  name: "downloadFile",
1325
1239
  isExported: true,
@@ -1429,7 +1343,7 @@ var _FileDownloadGenerator = class _FileDownloadGenerator {
1429
1343
 
1430
1344
  return fallbackFilename;`
1431
1345
  });
1432
- sourceFile.formatText();
1346
+ sourceFile.fixMissingImports().organizeImports().formatText();
1433
1347
  sourceFile.saveSync();
1434
1348
  }
1435
1349
  };
@@ -1450,34 +1364,6 @@ var _DateTransformerGenerator = class _DateTransformerGenerator {
1450
1364
  const sourceFile = this.project.createSourceFile(filePath, "", {
1451
1365
  overwrite: true
1452
1366
  });
1453
- sourceFile.addImportDeclaration({
1454
- namedImports: [
1455
- "HttpInterceptor",
1456
- "HttpRequest",
1457
- "HttpHandler",
1458
- "HttpEvent",
1459
- "HttpResponse"
1460
- ],
1461
- moduleSpecifier: "@angular/common/http"
1462
- });
1463
- sourceFile.addImportDeclaration({
1464
- namedImports: [
1465
- "Injectable"
1466
- ],
1467
- moduleSpecifier: "@angular/core"
1468
- });
1469
- sourceFile.addImportDeclaration({
1470
- namedImports: [
1471
- "Observable"
1472
- ],
1473
- moduleSpecifier: "rxjs"
1474
- });
1475
- sourceFile.addImportDeclaration({
1476
- namedImports: [
1477
- "map"
1478
- ],
1479
- moduleSpecifier: "rxjs/operators"
1480
- });
1481
1367
  sourceFile.addVariableStatement({
1482
1368
  isExported: true,
1483
1369
  declarationKind: import_ts_morph3.VariableDeclarationKind.Const,
@@ -1564,7 +1450,7 @@ var _DateTransformerGenerator = class _DateTransformerGenerator {
1564
1450
  }
1565
1451
  ]
1566
1452
  });
1567
- sourceFile.formatText();
1453
+ sourceFile.fixMissingImports().organizeImports().formatText();
1568
1454
  sourceFile.saveSync();
1569
1455
  }
1570
1456
  };
@@ -1847,33 +1733,9 @@ var _BaseInterceptorGenerator = class _BaseInterceptorGenerator {
1847
1733
  sourceFile.addImportDeclarations([
1848
1734
  {
1849
1735
  namedImports: [
1850
- "HttpEvent",
1851
- "HttpHandler",
1852
- "HttpInterceptor",
1853
- "HttpRequest",
1854
- "HttpContextToken"
1855
- ],
1856
- moduleSpecifier: "@angular/common/http"
1857
- },
1858
- {
1859
- namedImports: [
1860
- "inject",
1861
1736
  "Injectable"
1862
1737
  ],
1863
1738
  moduleSpecifier: "@angular/core"
1864
- },
1865
- {
1866
- namedImports: [
1867
- "Observable"
1868
- ],
1869
- moduleSpecifier: "rxjs"
1870
- },
1871
- {
1872
- namedImports: [
1873
- interceptorsTokenName,
1874
- clientContextTokenName
1875
- ],
1876
- moduleSpecifier: "../tokens"
1877
1739
  }
1878
1740
  ]);
1879
1741
  sourceFile.addClass({
@@ -1939,7 +1801,7 @@ var _BaseInterceptorGenerator = class _BaseInterceptorGenerator {
1939
1801
  }
1940
1802
  ]
1941
1803
  });
1942
- sourceFile.formatText();
1804
+ sourceFile.fixMissingImports().organizeImports().formatText();
1943
1805
  sourceFile.saveSync();
1944
1806
  }
1945
1807
  getInterceptorsTokenName() {
@@ -2174,9 +2036,11 @@ var path9 = __toESM(require("path"));
2174
2036
 
2175
2037
  // src/lib/generators/service/service-method/service-method-body.generator.ts
2176
2038
  var _ServiceMethodBodyGenerator = class _ServiceMethodBodyGenerator {
2177
- constructor(config) {
2039
+ constructor(config, parser) {
2178
2040
  __publicField(this, "config");
2041
+ __publicField(this, "parser");
2179
2042
  this.config = config;
2043
+ this.parser = parser;
2180
2044
  }
2181
2045
  generateMethodBody(operation) {
2182
2046
  const context = this.createGenerationContext(operation);
@@ -2195,11 +2059,16 @@ var _ServiceMethodBodyGenerator = class _ServiceMethodBodyGenerator {
2195
2059
  return !!((_b = (_a = operation.requestBody) == null ? void 0 : _a.content) == null ? void 0 : _b["multipart/form-data"]);
2196
2060
  }
2197
2061
  getFormDataFields(operation) {
2198
- var _a, _b, _c, _d;
2062
+ var _a, _b;
2199
2063
  if (!this.isMultipartFormData(operation)) {
2200
2064
  return [];
2201
2065
  }
2202
- const properties = ((_d = (_c = (_b = (_a = operation.requestBody) == null ? void 0 : _a.content) == null ? void 0 : _b["multipart/form-data"]) == null ? void 0 : _c.schema) == null ? void 0 : _d.properties) || {};
2066
+ const schema = (_b = (_a = operation.requestBody) == null ? void 0 : _a.content) == null ? void 0 : _b["multipart/form-data"].schema;
2067
+ let resolvedSchema = schema;
2068
+ if (schema == null ? void 0 : schema.$ref) {
2069
+ resolvedSchema = this.parser.resolveReference(schema.$ref);
2070
+ }
2071
+ const properties = (resolvedSchema == null ? void 0 : resolvedSchema.properties) || {};
2203
2072
  return Object.keys(properties);
2204
2073
  }
2205
2074
  createGenerationContext(operation) {
@@ -2266,17 +2135,37 @@ if (!headers.has('Content-Type')) {
2266
2135
  return headerCode;
2267
2136
  }
2268
2137
  generateMultipartFormData(operation, context) {
2138
+ var _a, _b;
2269
2139
  if (!context.isMultipart || context.formDataFields.length === 0) {
2270
2140
  return "";
2271
2141
  }
2142
+ const schema = (_b = (_a = operation.requestBody) == null ? void 0 : _a.content) == null ? void 0 : _b["multipart/form-data"].schema;
2143
+ let resolvedSchema = schema;
2144
+ if (schema == null ? void 0 : schema.$ref) {
2145
+ resolvedSchema = this.parser.resolveReference(schema.$ref);
2146
+ }
2147
+ const properties = (resolvedSchema == null ? void 0 : resolvedSchema.properties) || {};
2272
2148
  const formDataAppends = context.formDataFields.map((field) => {
2273
- var _a, _b, _c, _d, _e;
2274
- const fieldSchema = (_e = (_d = (_c = (_b = (_a = operation.requestBody) == null ? void 0 : _a.content) == null ? void 0 : _b["multipart/form-data"]) == null ? void 0 : _c.schema) == null ? void 0 : _d.properties) == null ? void 0 : _e[field];
2149
+ const fieldSchema = properties[field];
2275
2150
  const isFile = (fieldSchema == null ? void 0 : fieldSchema.type) === "string" && (fieldSchema == null ? void 0 : fieldSchema.format) === "binary";
2276
- const valueExpression = isFile ? field : `String(${field})`;
2277
- return `if (${field} !== undefined) {
2278
- formData.append('${field}', ${valueExpression});
2279
- }`;
2151
+ const isArray = (fieldSchema == null ? void 0 : fieldSchema.type) === "array";
2152
+ if (isArray) {
2153
+ const itemSchema = Array.isArray(fieldSchema.items) ? fieldSchema.items[0] : fieldSchema.items;
2154
+ const isFileArray = (itemSchema == null ? void 0 : itemSchema.type) === "string" && (itemSchema == null ? void 0 : itemSchema.format) === "binary";
2155
+ const valueExpression = isFileArray ? "item" : "String(item)";
2156
+ return `if (${field} !== undefined && Array.isArray(${field})) {
2157
+ ${field}.forEach((item) => {
2158
+ if (item !== undefined && item !== null) {
2159
+ formData.append('${field}', ${valueExpression});
2160
+ }
2161
+ });
2162
+ }`;
2163
+ } else {
2164
+ const valueExpression = isFile ? field : `String(${field})`;
2165
+ return `if (${field} !== undefined) {
2166
+ formData.append('${field}', ${valueExpression});
2167
+ }`;
2168
+ }
2280
2169
  }).join("\n");
2281
2170
  return `
2282
2171
  const formData = new FormData();
@@ -2353,9 +2242,11 @@ var ServiceMethodBodyGenerator = _ServiceMethodBodyGenerator;
2353
2242
 
2354
2243
  // src/lib/generators/service/service-method/service-method-params.generator.ts
2355
2244
  var _ServiceMethodParamsGenerator = class _ServiceMethodParamsGenerator {
2356
- constructor(config) {
2245
+ constructor(config, parser) {
2357
2246
  __publicField(this, "config");
2247
+ __publicField(this, "parser");
2358
2248
  this.config = config;
2249
+ this.parser = parser;
2359
2250
  }
2360
2251
  generateMethodParameters(operation) {
2361
2252
  const params = this.generateApiParameters(operation);
@@ -2375,7 +2266,7 @@ var _ServiceMethodParamsGenerator = class _ServiceMethodParamsGenerator {
2375
2266
  return uniqueParams;
2376
2267
  }
2377
2268
  generateApiParameters(operation) {
2378
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2269
+ var _a, _b, _c, _d, _e, _f, _g;
2379
2270
  const params = [];
2380
2271
  const pathParams = ((_a = operation.parameters) == null ? void 0 : _a.filter((p) => p.in === "path")) || [];
2381
2272
  pathParams.forEach((param) => {
@@ -2386,15 +2277,21 @@ var _ServiceMethodParamsGenerator = class _ServiceMethodParamsGenerator {
2386
2277
  });
2387
2278
  });
2388
2279
  if (operation.requestBody && ((_c = (_b = operation.requestBody) == null ? void 0 : _b.content) == null ? void 0 : _c["multipart/form-data"])) {
2389
- Object.entries((_g = (_f = (_e = (_d = operation.requestBody) == null ? void 0 : _d.content) == null ? void 0 : _e["multipart/form-data"].schema) == null ? void 0 : _f.properties) != null ? _g : {}).forEach(([key, value]) => {
2280
+ const schema = operation.requestBody.content["multipart/form-data"].schema;
2281
+ let resolvedSchema = schema;
2282
+ if (schema == null ? void 0 : schema.$ref) {
2283
+ resolvedSchema = this.parser.resolveReference(schema.$ref);
2284
+ }
2285
+ Object.entries((_d = resolvedSchema == null ? void 0 : resolvedSchema.properties) != null ? _d : {}).forEach(([key, value]) => {
2286
+ var _a2;
2390
2287
  params.push({
2391
2288
  name: key,
2392
2289
  type: getTypeScriptType(value, this.config, value.nullable),
2393
- hasQuestionToken: !value.required
2290
+ hasQuestionToken: !((_a2 = resolvedSchema == null ? void 0 : resolvedSchema.required) == null ? void 0 : _a2.includes(key))
2394
2291
  });
2395
2292
  });
2396
2293
  }
2397
- if (operation.requestBody && ((_i = (_h = operation.requestBody) == null ? void 0 : _h.content) == null ? void 0 : _i["application/json"])) {
2294
+ if (operation.requestBody && ((_f = (_e = operation.requestBody) == null ? void 0 : _e.content) == null ? void 0 : _f["application/json"])) {
2398
2295
  const bodyType = this.getRequestBodyType(operation.requestBody);
2399
2296
  const isInterface = isDataTypeInterface(bodyType);
2400
2297
  params.push({
@@ -2403,7 +2300,7 @@ var _ServiceMethodParamsGenerator = class _ServiceMethodParamsGenerator {
2403
2300
  hasQuestionToken: !operation.requestBody.required
2404
2301
  });
2405
2302
  }
2406
- const queryParams = ((_j = operation.parameters) == null ? void 0 : _j.filter((p) => p.in === "query")) || [];
2303
+ const queryParams = ((_g = operation.parameters) == null ? void 0 : _g.filter((p) => p.in === "query")) || [];
2407
2304
  queryParams.forEach((param) => {
2408
2305
  params.push({
2409
2306
  name: param.name,
@@ -2453,12 +2350,12 @@ var ServiceMethodParamsGenerator = _ServiceMethodParamsGenerator;
2453
2350
 
2454
2351
  // src/lib/generators/service/service-method/service-method-overloads.generator.ts
2455
2352
  var _ServiceMethodOverloadsGenerator = class _ServiceMethodOverloadsGenerator {
2456
- constructor(config) {
2353
+ constructor(config, parser) {
2457
2354
  __publicField(this, "config");
2458
2355
  __publicField(this, "paramsGenerator");
2459
2356
  __publicField(this, "responseDataType", "any");
2460
2357
  this.config = config;
2461
- this.paramsGenerator = new ServiceMethodParamsGenerator(config);
2358
+ this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
2462
2359
  }
2463
2360
  generateMethodOverloads(operation) {
2464
2361
  const observeTypes = [
@@ -2570,15 +2467,15 @@ var ServiceMethodOverloadsGenerator = _ServiceMethodOverloadsGenerator;
2570
2467
 
2571
2468
  // src/lib/generators/service/service-method.generator.ts
2572
2469
  var _ServiceMethodGenerator = class _ServiceMethodGenerator {
2573
- constructor(config) {
2470
+ constructor(config, parser) {
2574
2471
  __publicField(this, "config");
2575
2472
  __publicField(this, "bodyGenerator");
2576
2473
  __publicField(this, "overloadsGenerator");
2577
2474
  __publicField(this, "paramsGenerator");
2578
2475
  this.config = config;
2579
- this.bodyGenerator = new ServiceMethodBodyGenerator(config);
2580
- this.overloadsGenerator = new ServiceMethodOverloadsGenerator(config);
2581
- this.paramsGenerator = new ServiceMethodParamsGenerator(config);
2476
+ this.bodyGenerator = new ServiceMethodBodyGenerator(config, parser);
2477
+ this.overloadsGenerator = new ServiceMethodOverloadsGenerator(config, parser);
2478
+ this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
2582
2479
  }
2583
2480
  addServiceMethod(serviceClass, operation) {
2584
2481
  const methodName = this.generateMethodName(operation);
@@ -2641,7 +2538,7 @@ var _ServiceGenerator = class _ServiceGenerator {
2641
2538
  const versionInfo = this.parser.getSpecVersion();
2642
2539
  throw new Error(`Invalid or unsupported specification format. Expected OpenAPI 3.x or Swagger 2.x. ${versionInfo ? `Found: ${versionInfo.type} ${versionInfo.version}` : "No version info found"}`);
2643
2540
  }
2644
- this.methodGenerator = new ServiceMethodGenerator(config);
2541
+ this.methodGenerator = new ServiceMethodGenerator(config, parser);
2645
2542
  }
2646
2543
  generate(outputRoot) {
2647
2544
  return __async(this, null, function* () {
@@ -2682,63 +2579,21 @@ var _ServiceGenerator = class _ServiceGenerator {
2682
2579
  const sourceFile = this.project.createSourceFile(filePath, "", {
2683
2580
  overwrite: true
2684
2581
  });
2685
- const usedTypes = collectUsedTypes(operations);
2686
- this.addImports(sourceFile, usedTypes);
2582
+ this.addImports(sourceFile);
2687
2583
  this.addServiceClass(sourceFile, controllerName, operations);
2688
- sourceFile.formatText();
2584
+ sourceFile.fixMissingImports().organizeImports().fixUnusedIdentifiers().formatText();
2689
2585
  sourceFile.saveSync();
2690
2586
  });
2691
2587
  }
2692
- addImports(sourceFile, usedTypes) {
2693
- const basePathTokenName = getBasePathTokenName(this.config.clientName);
2694
- const clientContextTokenName = getClientContextTokenName(this.config.clientName);
2588
+ addImports(sourceFile) {
2695
2589
  sourceFile.addImportDeclarations([
2696
2590
  {
2697
2591
  namedImports: [
2698
- "Injectable",
2699
- "inject"
2592
+ "Injectable"
2700
2593
  ],
2701
2594
  moduleSpecifier: "@angular/core"
2702
- },
2703
- {
2704
- namedImports: [
2705
- "HttpClient",
2706
- "HttpParams",
2707
- "HttpHeaders",
2708
- "HttpContext",
2709
- "HttpResponse",
2710
- "HttpEvent",
2711
- "HttpContextToken"
2712
- ],
2713
- moduleSpecifier: "@angular/common/http"
2714
- },
2715
- {
2716
- namedImports: [
2717
- "Observable",
2718
- "map"
2719
- ],
2720
- moduleSpecifier: "rxjs"
2721
- },
2722
- {
2723
- namedImports: [
2724
- basePathTokenName,
2725
- clientContextTokenName
2726
- ],
2727
- moduleSpecifier: "../tokens"
2728
- },
2729
- {
2730
- namedImports: [
2731
- "HttpParamsBuilder"
2732
- ],
2733
- moduleSpecifier: "../index"
2734
2595
  }
2735
2596
  ]);
2736
- if (usedTypes.size > 0) {
2737
- sourceFile.addImportDeclaration({
2738
- namedImports: Array.from(usedTypes).sort(),
2739
- moduleSpecifier: "../models"
2740
- });
2741
- }
2742
2597
  }
2743
2598
  addServiceClass(sourceFile, controllerName, operations) {
2744
2599
  const className = `${controllerName}Service`;
@@ -2892,15 +2747,15 @@ function generateFromConfig(config) {
2892
2747
  fileDownloadHelper.generate(outputPath);
2893
2748
  const httpParamsBuilderGenerator = new HttpParamsBuilderGenerator(project);
2894
2749
  httpParamsBuilderGenerator.generate(outputPath);
2750
+ const providerGenerator = new ProviderGenerator(project, config);
2751
+ providerGenerator.generate(outputPath);
2752
+ const baseInterceptorGenerator = new BaseInterceptorGenerator(project, config.clientName);
2753
+ baseInterceptorGenerator.generate(outputPath);
2895
2754
  const serviceGenerator = new ServiceGenerator(swaggerParser, project, config);
2896
2755
  yield serviceGenerator.generate(outputPath);
2897
2756
  const indexGenerator = new ServiceIndexGenerator(project);
2898
2757
  indexGenerator.generateIndex(outputPath);
2899
2758
  console.log(`\u2705 Angular services generated`);
2900
- const providerGenerator = new ProviderGenerator(project, config);
2901
- providerGenerator.generate(outputPath);
2902
- const baseInterceptorGenerator = new BaseInterceptorGenerator(project, config.clientName);
2903
- baseInterceptorGenerator.generate(outputPath);
2904
2759
  }
2905
2760
  if ((_b = config.plugins) == null ? void 0 : _b.length) {
2906
2761
  for (const plugin of config.plugins) {
@@ -2944,7 +2799,6 @@ __name(generateFromConfig, "generateFromConfig");
2944
2799
  SwaggerParser,
2945
2800
  TYPE_GENERATOR_HEADER_COMMENT,
2946
2801
  camelCase,
2947
- collectUsedTypes,
2948
2802
  escapeString,
2949
2803
  extractPaths,
2950
2804
  generateFromConfig,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ng-openapi",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "Generate Angular services and TypeScript types from OpenAPI/Swagger specifications",
5
5
  "keywords": [
6
6
  "ng-openapi",