@ng-openapi/http-resource 0.0.11 → 0.0.12

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/index.cjs +48 -205
  2. package/index.d.ts +2 -3
  3. package/index.js +48 -205
  4. package/package.json +1 -1
package/index.cjs CHANGED
@@ -40,12 +40,11 @@ var import_ts_morph = require("ts-morph");
40
40
 
41
41
  // ../../shared/src/utils/string.utils.ts
42
42
  function camelCase(str2) {
43
- const cleaned = str2.replace(/[-_](\w)/g, (_, c) => c.toUpperCase());
44
- return cleaned.charAt(0).toLowerCase() + cleaned.slice(1);
43
+ return str2.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toLowerCase());
45
44
  }
46
45
  __name(camelCase, "camelCase");
47
46
  function pascalCase(str2) {
48
- return str2.replace(/(?:^|[-_])([a-z])/g, (_, char) => char.toUpperCase());
47
+ return str2.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toUpperCase());
49
48
  }
50
49
  __name(pascalCase, "pascalCase");
51
50
 
@@ -216,12 +215,12 @@ function extractPaths(swaggerPaths = {}, methods = [
216
215
  "head"
217
216
  ]) {
218
217
  const paths = [];
219
- Object.entries(swaggerPaths).forEach(([path4, pathItem]) => {
218
+ Object.entries(swaggerPaths).forEach(([path3, pathItem]) => {
220
219
  methods.forEach((method) => {
221
220
  if (pathItem[method]) {
222
221
  const operation = pathItem[method];
223
222
  paths.push({
224
- path: path4,
223
+ path: path3,
225
224
  method: method.toUpperCase(),
226
225
  operationId: operation.operationId,
227
226
  summary: operation.summary,
@@ -369,6 +368,12 @@ function inferResponseTypeFromContentType(contentType) {
369
368
  __name(inferResponseTypeFromContentType, "inferResponseTypeFromContentType");
370
369
  function getResponseType(response, config) {
371
370
  const responseType = getResponseTypeFromResponse(response);
371
+ const content = response.content || {};
372
+ for (const [contentType, mediaType] of Object.entries(content)) {
373
+ if (mediaType?.schema) {
374
+ return getTypeScriptType(mediaType.schema, config, mediaType.schema.nullable);
375
+ }
376
+ }
372
377
  switch (responseType) {
373
378
  case "blob":
374
379
  return "Blob";
@@ -376,15 +381,6 @@ function getResponseType(response, config) {
376
381
  return "ArrayBuffer";
377
382
  case "text":
378
383
  return "string";
379
- case "json": {
380
- const content = response.content || {};
381
- for (const [contentType, mediaType] of Object.entries(content)) {
382
- if (inferResponseTypeFromContentType(contentType) === "json" && mediaType?.schema) {
383
- return getTypeScriptType(mediaType.schema, config, mediaType.schema.nullable);
384
- }
385
- }
386
- return "any";
387
- }
388
384
  default:
389
385
  return "any";
390
386
  }
@@ -423,10 +419,6 @@ var HTTP_RESOURCE_GENERATOR_HEADER_COMMENT = /* @__PURE__ */ __name((resourceNam
423
419
  */
424
420
  `, "HTTP_RESOURCE_GENERATOR_HEADER_COMMENT");
425
421
 
426
- // ../../shared/src/core/swagger-parser.ts
427
- var fs = __toESM(require("fs"));
428
- var path = __toESM(require("path"));
429
-
430
422
  // ../../../node_modules/js-yaml/dist/js-yaml.mjs
431
423
  function isNothing(subject) {
432
424
  return typeof subject === "undefined" || subject === null;
@@ -3125,168 +3117,8 @@ var safeLoad = renamed("safeLoad", "load");
3125
3117
  var safeLoadAll = renamed("safeLoadAll", "loadAll");
3126
3118
  var safeDump = renamed("safeDump", "dump");
3127
3119
 
3128
- // ../../shared/src/utils/functions/is-url.ts
3129
- function isUrl(input) {
3130
- try {
3131
- const url = new URL(input);
3132
- return [
3133
- "http:",
3134
- "https:"
3135
- ].includes(url.protocol);
3136
- } catch {
3137
- return false;
3138
- }
3139
- }
3140
- __name(isUrl, "isUrl");
3141
-
3142
- // ../../shared/src/core/swagger-parser.ts
3143
- var SwaggerParser = class _SwaggerParser {
3144
- static {
3145
- __name(this, "SwaggerParser");
3146
- }
3147
- spec;
3148
- constructor(spec, config) {
3149
- const isInputValid = config.validateInput?.(spec) ?? true;
3150
- if (!isInputValid) {
3151
- throw new Error("Swagger spec is not valid. Check your `validateInput` condition.");
3152
- }
3153
- this.spec = spec;
3154
- }
3155
- static async create(swaggerPathOrUrl, config) {
3156
- const swaggerContent = await _SwaggerParser.loadContent(swaggerPathOrUrl);
3157
- const spec = _SwaggerParser.parseSpecContent(swaggerContent, swaggerPathOrUrl);
3158
- return new _SwaggerParser(spec, config);
3159
- }
3160
- static async loadContent(pathOrUrl) {
3161
- if (isUrl(pathOrUrl)) {
3162
- return await _SwaggerParser.fetchUrlContent(pathOrUrl);
3163
- } else {
3164
- return fs.readFileSync(pathOrUrl, "utf8");
3165
- }
3166
- }
3167
- static async fetchUrlContent(url) {
3168
- try {
3169
- const response = await fetch(url, {
3170
- method: "GET",
3171
- headers: {
3172
- Accept: "application/json, application/yaml, text/yaml, text/plain, */*",
3173
- "User-Agent": "ng-openapi"
3174
- },
3175
- // 30 second timeout
3176
- signal: AbortSignal.timeout(3e4)
3177
- });
3178
- if (!response.ok) {
3179
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
3180
- }
3181
- const content = await response.text();
3182
- if (!content || content.trim() === "") {
3183
- throw new Error(`Empty response from URL: ${url}`);
3184
- }
3185
- return content;
3186
- } catch (error) {
3187
- let errorMessage = `Failed to fetch content from URL: ${url}`;
3188
- if (error.name === "AbortError") {
3189
- errorMessage += " - Request timeout (30s)";
3190
- } else if (error.message) {
3191
- errorMessage += ` - ${error.message}`;
3192
- }
3193
- throw new Error(errorMessage);
3194
- }
3195
- }
3196
- static parseSpecContent(content, pathOrUrl) {
3197
- let format;
3198
- if (isUrl(pathOrUrl)) {
3199
- const urlPath = new URL(pathOrUrl).pathname.toLowerCase();
3200
- if (urlPath.endsWith(".json")) {
3201
- format = "json";
3202
- } else if (urlPath.endsWith(".yaml") || urlPath.endsWith(".yml")) {
3203
- format = "yaml";
3204
- } else {
3205
- format = _SwaggerParser.detectFormat(content);
3206
- }
3207
- } else {
3208
- const extension = path.extname(pathOrUrl).toLowerCase();
3209
- switch (extension) {
3210
- case ".json":
3211
- format = "json";
3212
- break;
3213
- case ".yaml":
3214
- format = "yaml";
3215
- break;
3216
- case ".yml":
3217
- format = "yml";
3218
- break;
3219
- default:
3220
- format = _SwaggerParser.detectFormat(content);
3221
- }
3222
- }
3223
- try {
3224
- switch (format) {
3225
- case "json":
3226
- return JSON.parse(content);
3227
- case "yaml":
3228
- case "yml":
3229
- return load(content);
3230
- default:
3231
- throw new Error(`Unable to determine format for: ${pathOrUrl}`);
3232
- }
3233
- } catch (error) {
3234
- throw new Error(`Failed to parse ${format.toUpperCase()} content from: ${pathOrUrl}. Error: ${error instanceof Error ? error.message : error}`);
3235
- }
3236
- }
3237
- static detectFormat(content) {
3238
- const trimmed = content.trim();
3239
- if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
3240
- return "json";
3241
- }
3242
- if (trimmed.includes("openapi:") || trimmed.includes("swagger:") || trimmed.includes("---") || /^[a-zA-Z][a-zA-Z0-9_]*\s*:/.test(trimmed)) {
3243
- return "yaml";
3244
- }
3245
- return "json";
3246
- }
3247
- getDefinitions() {
3248
- return this.spec.definitions || this.spec.components?.schemas || {};
3249
- }
3250
- getDefinition(name) {
3251
- const definitions = this.getDefinitions();
3252
- return definitions[name];
3253
- }
3254
- resolveReference(ref) {
3255
- const parts = ref.split("/");
3256
- const definitionName = parts[parts.length - 1];
3257
- return this.getDefinition(definitionName);
3258
- }
3259
- getAllDefinitionNames() {
3260
- return Object.keys(this.getDefinitions());
3261
- }
3262
- getSpec() {
3263
- return this.spec;
3264
- }
3265
- getPaths() {
3266
- return this.spec.paths || {};
3267
- }
3268
- isValidSpec() {
3269
- return !!(this.spec.swagger && this.spec.swagger.startsWith("2.") || this.spec.openapi && this.spec.openapi.startsWith("3."));
3270
- }
3271
- getSpecVersion() {
3272
- if (this.spec.swagger) {
3273
- return {
3274
- type: "swagger",
3275
- version: this.spec.swagger
3276
- };
3277
- }
3278
- if (this.spec.openapi) {
3279
- return {
3280
- type: "openapi",
3281
- version: this.spec.openapi
3282
- };
3283
- }
3284
- return null;
3285
- }
3286
- };
3287
-
3288
3120
  // src/lib/http-resource.generator.ts
3289
- var path3 = __toESM(require("path"), 1);
3121
+ var path2 = __toESM(require("path"), 1);
3290
3122
 
3291
3123
  // src/lib/http-resource-method/http-resource-method-body.generator.ts
3292
3124
  var HttpResourceMethodBodyGenerator = class {
@@ -3526,17 +3358,32 @@ var HttpResourceMethodGenerator = class {
3526
3358
  parameters,
3527
3359
  returnType,
3528
3360
  statements: methodBody,
3529
- overloads
3361
+ overloads,
3362
+ docs: operation.description ? [
3363
+ operation.description
3364
+ ] : void 0
3530
3365
  });
3531
3366
  }
3532
3367
  generateMethodName(operation) {
3368
+ if (this.config.options.customizeMethodName) {
3369
+ if (operation.operationId == null) {
3370
+ throw new Error(`Operation ID is required for method name customization of operation: (${operation.method}) ${operation.path}`);
3371
+ }
3372
+ return this.config.options.customizeMethodName(operation.operationId);
3373
+ } else {
3374
+ return this.defaultNameGenerator(operation);
3375
+ }
3376
+ }
3377
+ defaultNameGenerator(operation) {
3533
3378
  if (operation.operationId) {
3534
3379
  return camelCase(operation.operationId);
3535
3380
  }
3536
- const method = operation.method.toLowerCase();
3537
- const pathParts = operation.path.split("/").filter((p) => p && !p.startsWith("{"));
3538
- const resource = pathParts[pathParts.length - 1] || "resource";
3539
- return `${method}${pascalCase(resource)}`;
3381
+ const method = pascalCase(operation.method.toLowerCase());
3382
+ const pathParts = operation.path.split("/").map((str2) => {
3383
+ return pascalCase(pascalCase(str2).replace(/[^a-zA-Z0-9]/g, ""));
3384
+ });
3385
+ const resource = pathParts.join("") || "resource";
3386
+ return `${camelCase(resource)}${method}`;
3540
3387
  }
3541
3388
  generateReturnType(operation) {
3542
3389
  const response = operation.responses?.["200"] || operation.responses?.["201"] || operation.responses?.["204"];
@@ -3579,8 +3426,8 @@ var HttpResourceMethodGenerator = class {
3579
3426
  };
3580
3427
 
3581
3428
  // src/lib/http-resource-index.generator.ts
3582
- var fs2 = __toESM(require("fs"), 1);
3583
- var path2 = __toESM(require("path"), 1);
3429
+ var fs = __toESM(require("fs"), 1);
3430
+ var path = __toESM(require("path"), 1);
3584
3431
  var HttpResourceIndexGenerator = class {
3585
3432
  static {
3586
3433
  __name(this, "HttpResourceIndexGenerator");
@@ -3590,13 +3437,13 @@ var HttpResourceIndexGenerator = class {
3590
3437
  this.project = project;
3591
3438
  }
3592
3439
  generateIndex(outputRoot) {
3593
- const servicesDir = path2.join(outputRoot, "resources");
3594
- const indexPath = path2.join(servicesDir, "index.ts");
3440
+ const servicesDir = path.join(outputRoot, "resources");
3441
+ const indexPath = path.join(servicesDir, "index.ts");
3595
3442
  const sourceFile = this.project.createSourceFile(indexPath, "", {
3596
3443
  overwrite: true
3597
3444
  });
3598
3445
  sourceFile.insertText(0, SERVICE_INDEX_GENERATOR_HEADER_COMMENT);
3599
- const serviceFiles = fs2.readdirSync(servicesDir).filter((file) => file.endsWith(".resource.ts")).map((file) => file.replace(".resource.ts", ""));
3446
+ const serviceFiles = fs.readdirSync(servicesDir).filter((file) => file.endsWith(".resource.ts")).map((file) => file.replace(".resource.ts", ""));
3600
3447
  serviceFiles.forEach((serviceName) => {
3601
3448
  const className = pascalCase(serviceName) + "Resource";
3602
3449
  sourceFile.addExportDeclaration({
@@ -3612,7 +3459,7 @@ var HttpResourceIndexGenerator = class {
3612
3459
  };
3613
3460
 
3614
3461
  // src/lib/http-resource.generator.ts
3615
- var HttpResourceGenerator = class _HttpResourceGenerator {
3462
+ var HttpResourceGenerator = class {
3616
3463
  static {
3617
3464
  __name(this, "HttpResourceGenerator");
3618
3465
  }
@@ -3634,12 +3481,8 @@ var HttpResourceGenerator = class _HttpResourceGenerator {
3634
3481
  }
3635
3482
  this.methodGenerator = new HttpResourceMethodGenerator(config);
3636
3483
  }
3637
- static async create(swaggerPathOrUrl, project, config) {
3638
- const parser = await SwaggerParser.create(swaggerPathOrUrl, config);
3639
- return new _HttpResourceGenerator(parser, project, config);
3640
- }
3641
- generate(outputRoot) {
3642
- const outputDir = path3.join(outputRoot, "resources");
3484
+ async generate(outputRoot) {
3485
+ const outputDir = path2.join(outputRoot, "resources");
3643
3486
  const paths = extractPaths(this.spec.paths, [
3644
3487
  "get"
3645
3488
  ]);
@@ -3648,19 +3491,19 @@ var HttpResourceGenerator = class _HttpResourceGenerator {
3648
3491
  return;
3649
3492
  }
3650
3493
  const controllerGroups = this.groupPathsByController(paths);
3651
- Object.entries(controllerGroups).forEach(([resourceName, operations]) => {
3494
+ await Promise.all(Object.entries(controllerGroups).map(([resourceName, operations]) => {
3652
3495
  this.generateServiceFile(resourceName, operations, outputDir);
3653
- });
3496
+ }));
3654
3497
  this.indexGenerator.generateIndex(outputRoot);
3655
3498
  }
3656
3499
  groupPathsByController(paths) {
3657
3500
  const groups = {};
3658
- paths.forEach((path4) => {
3501
+ paths.forEach((path3) => {
3659
3502
  let controllerName = "Default";
3660
- if (path4.tags && path4.tags.length > 0) {
3661
- controllerName = path4.tags[0];
3503
+ if (path3.tags && path3.tags.length > 0) {
3504
+ controllerName = path3.tags[0];
3662
3505
  } else {
3663
- const pathParts = path4.path.split("/").filter((p) => p && !p.startsWith("{"));
3506
+ const pathParts = path3.path.split("/").filter((p) => p && !p.startsWith("{"));
3664
3507
  if (pathParts.length > 1) {
3665
3508
  controllerName = pascalCase(pathParts[1]);
3666
3509
  }
@@ -3670,13 +3513,13 @@ var HttpResourceGenerator = class _HttpResourceGenerator {
3670
3513
  if (!groups[controllerName]) {
3671
3514
  groups[controllerName] = [];
3672
3515
  }
3673
- groups[controllerName].push(path4);
3516
+ groups[controllerName].push(path3);
3674
3517
  });
3675
3518
  return groups;
3676
3519
  }
3677
- generateServiceFile(resourceName, operations, outputDir) {
3520
+ async generateServiceFile(resourceName, operations, outputDir) {
3678
3521
  const fileName = `${camelCase(resourceName).replace(/Resource/, "")}.resource.ts`;
3679
- const filePath = path3.join(outputDir, fileName);
3522
+ const filePath = path2.join(outputDir, fileName);
3680
3523
  const sourceFile = this.project.createSourceFile(filePath, "", {
3681
3524
  overwrite: true
3682
3525
  });
package/index.d.ts CHANGED
@@ -106,7 +106,7 @@ interface IPluginGenerator {
106
106
  /**
107
107
  * Generate code files
108
108
  */
109
- generate(outputRoot: string): void;
109
+ generate(outputRoot: string): Promise<void>;
110
110
  }
111
111
 
112
112
  interface GeneratorConfig {
@@ -145,8 +145,7 @@ declare class HttpResourceGenerator implements IPluginGenerator {
145
145
  private methodGenerator;
146
146
  private indexGenerator;
147
147
  constructor(parser: SwaggerParser, project: Project, config: GeneratorConfig);
148
- static create(swaggerPathOrUrl: string, project: Project, config: GeneratorConfig): Promise<HttpResourceGenerator>;
149
- generate(outputRoot: string): void;
148
+ generate(outputRoot: string): Promise<void>;
150
149
  private groupPathsByController;
151
150
  private generateServiceFile;
152
151
  private addImports;
package/index.js CHANGED
@@ -6,12 +6,11 @@ import { Scope } from "ts-morph";
6
6
 
7
7
  // ../../shared/src/utils/string.utils.ts
8
8
  function camelCase(str2) {
9
- const cleaned = str2.replace(/[-_](\w)/g, (_, c) => c.toUpperCase());
10
- return cleaned.charAt(0).toLowerCase() + cleaned.slice(1);
9
+ return str2.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toLowerCase());
11
10
  }
12
11
  __name(camelCase, "camelCase");
13
12
  function pascalCase(str2) {
14
- return str2.replace(/(?:^|[-_])([a-z])/g, (_, char) => char.toUpperCase());
13
+ return str2.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toUpperCase());
15
14
  }
16
15
  __name(pascalCase, "pascalCase");
17
16
 
@@ -182,12 +181,12 @@ function extractPaths(swaggerPaths = {}, methods = [
182
181
  "head"
183
182
  ]) {
184
183
  const paths = [];
185
- Object.entries(swaggerPaths).forEach(([path4, pathItem]) => {
184
+ Object.entries(swaggerPaths).forEach(([path3, pathItem]) => {
186
185
  methods.forEach((method) => {
187
186
  if (pathItem[method]) {
188
187
  const operation = pathItem[method];
189
188
  paths.push({
190
- path: path4,
189
+ path: path3,
191
190
  method: method.toUpperCase(),
192
191
  operationId: operation.operationId,
193
192
  summary: operation.summary,
@@ -335,6 +334,12 @@ function inferResponseTypeFromContentType(contentType) {
335
334
  __name(inferResponseTypeFromContentType, "inferResponseTypeFromContentType");
336
335
  function getResponseType(response, config) {
337
336
  const responseType = getResponseTypeFromResponse(response);
337
+ const content = response.content || {};
338
+ for (const [contentType, mediaType] of Object.entries(content)) {
339
+ if (mediaType?.schema) {
340
+ return getTypeScriptType(mediaType.schema, config, mediaType.schema.nullable);
341
+ }
342
+ }
338
343
  switch (responseType) {
339
344
  case "blob":
340
345
  return "Blob";
@@ -342,15 +347,6 @@ function getResponseType(response, config) {
342
347
  return "ArrayBuffer";
343
348
  case "text":
344
349
  return "string";
345
- case "json": {
346
- const content = response.content || {};
347
- for (const [contentType, mediaType] of Object.entries(content)) {
348
- if (inferResponseTypeFromContentType(contentType) === "json" && mediaType?.schema) {
349
- return getTypeScriptType(mediaType.schema, config, mediaType.schema.nullable);
350
- }
351
- }
352
- return "any";
353
- }
354
350
  default:
355
351
  return "any";
356
352
  }
@@ -389,10 +385,6 @@ var HTTP_RESOURCE_GENERATOR_HEADER_COMMENT = /* @__PURE__ */ __name((resourceNam
389
385
  */
390
386
  `, "HTTP_RESOURCE_GENERATOR_HEADER_COMMENT");
391
387
 
392
- // ../../shared/src/core/swagger-parser.ts
393
- import * as fs from "fs";
394
- import * as path from "path";
395
-
396
388
  // ../../../node_modules/js-yaml/dist/js-yaml.mjs
397
389
  function isNothing(subject) {
398
390
  return typeof subject === "undefined" || subject === null;
@@ -3091,168 +3083,8 @@ var safeLoad = renamed("safeLoad", "load");
3091
3083
  var safeLoadAll = renamed("safeLoadAll", "loadAll");
3092
3084
  var safeDump = renamed("safeDump", "dump");
3093
3085
 
3094
- // ../../shared/src/utils/functions/is-url.ts
3095
- function isUrl(input) {
3096
- try {
3097
- const url = new URL(input);
3098
- return [
3099
- "http:",
3100
- "https:"
3101
- ].includes(url.protocol);
3102
- } catch {
3103
- return false;
3104
- }
3105
- }
3106
- __name(isUrl, "isUrl");
3107
-
3108
- // ../../shared/src/core/swagger-parser.ts
3109
- var SwaggerParser = class _SwaggerParser {
3110
- static {
3111
- __name(this, "SwaggerParser");
3112
- }
3113
- spec;
3114
- constructor(spec, config) {
3115
- const isInputValid = config.validateInput?.(spec) ?? true;
3116
- if (!isInputValid) {
3117
- throw new Error("Swagger spec is not valid. Check your `validateInput` condition.");
3118
- }
3119
- this.spec = spec;
3120
- }
3121
- static async create(swaggerPathOrUrl, config) {
3122
- const swaggerContent = await _SwaggerParser.loadContent(swaggerPathOrUrl);
3123
- const spec = _SwaggerParser.parseSpecContent(swaggerContent, swaggerPathOrUrl);
3124
- return new _SwaggerParser(spec, config);
3125
- }
3126
- static async loadContent(pathOrUrl) {
3127
- if (isUrl(pathOrUrl)) {
3128
- return await _SwaggerParser.fetchUrlContent(pathOrUrl);
3129
- } else {
3130
- return fs.readFileSync(pathOrUrl, "utf8");
3131
- }
3132
- }
3133
- static async fetchUrlContent(url) {
3134
- try {
3135
- const response = await fetch(url, {
3136
- method: "GET",
3137
- headers: {
3138
- Accept: "application/json, application/yaml, text/yaml, text/plain, */*",
3139
- "User-Agent": "ng-openapi"
3140
- },
3141
- // 30 second timeout
3142
- signal: AbortSignal.timeout(3e4)
3143
- });
3144
- if (!response.ok) {
3145
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
3146
- }
3147
- const content = await response.text();
3148
- if (!content || content.trim() === "") {
3149
- throw new Error(`Empty response from URL: ${url}`);
3150
- }
3151
- return content;
3152
- } catch (error) {
3153
- let errorMessage = `Failed to fetch content from URL: ${url}`;
3154
- if (error.name === "AbortError") {
3155
- errorMessage += " - Request timeout (30s)";
3156
- } else if (error.message) {
3157
- errorMessage += ` - ${error.message}`;
3158
- }
3159
- throw new Error(errorMessage);
3160
- }
3161
- }
3162
- static parseSpecContent(content, pathOrUrl) {
3163
- let format;
3164
- if (isUrl(pathOrUrl)) {
3165
- const urlPath = new URL(pathOrUrl).pathname.toLowerCase();
3166
- if (urlPath.endsWith(".json")) {
3167
- format = "json";
3168
- } else if (urlPath.endsWith(".yaml") || urlPath.endsWith(".yml")) {
3169
- format = "yaml";
3170
- } else {
3171
- format = _SwaggerParser.detectFormat(content);
3172
- }
3173
- } else {
3174
- const extension = path.extname(pathOrUrl).toLowerCase();
3175
- switch (extension) {
3176
- case ".json":
3177
- format = "json";
3178
- break;
3179
- case ".yaml":
3180
- format = "yaml";
3181
- break;
3182
- case ".yml":
3183
- format = "yml";
3184
- break;
3185
- default:
3186
- format = _SwaggerParser.detectFormat(content);
3187
- }
3188
- }
3189
- try {
3190
- switch (format) {
3191
- case "json":
3192
- return JSON.parse(content);
3193
- case "yaml":
3194
- case "yml":
3195
- return load(content);
3196
- default:
3197
- throw new Error(`Unable to determine format for: ${pathOrUrl}`);
3198
- }
3199
- } catch (error) {
3200
- throw new Error(`Failed to parse ${format.toUpperCase()} content from: ${pathOrUrl}. Error: ${error instanceof Error ? error.message : error}`);
3201
- }
3202
- }
3203
- static detectFormat(content) {
3204
- const trimmed = content.trim();
3205
- if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
3206
- return "json";
3207
- }
3208
- if (trimmed.includes("openapi:") || trimmed.includes("swagger:") || trimmed.includes("---") || /^[a-zA-Z][a-zA-Z0-9_]*\s*:/.test(trimmed)) {
3209
- return "yaml";
3210
- }
3211
- return "json";
3212
- }
3213
- getDefinitions() {
3214
- return this.spec.definitions || this.spec.components?.schemas || {};
3215
- }
3216
- getDefinition(name) {
3217
- const definitions = this.getDefinitions();
3218
- return definitions[name];
3219
- }
3220
- resolveReference(ref) {
3221
- const parts = ref.split("/");
3222
- const definitionName = parts[parts.length - 1];
3223
- return this.getDefinition(definitionName);
3224
- }
3225
- getAllDefinitionNames() {
3226
- return Object.keys(this.getDefinitions());
3227
- }
3228
- getSpec() {
3229
- return this.spec;
3230
- }
3231
- getPaths() {
3232
- return this.spec.paths || {};
3233
- }
3234
- isValidSpec() {
3235
- return !!(this.spec.swagger && this.spec.swagger.startsWith("2.") || this.spec.openapi && this.spec.openapi.startsWith("3."));
3236
- }
3237
- getSpecVersion() {
3238
- if (this.spec.swagger) {
3239
- return {
3240
- type: "swagger",
3241
- version: this.spec.swagger
3242
- };
3243
- }
3244
- if (this.spec.openapi) {
3245
- return {
3246
- type: "openapi",
3247
- version: this.spec.openapi
3248
- };
3249
- }
3250
- return null;
3251
- }
3252
- };
3253
-
3254
3086
  // src/lib/http-resource.generator.ts
3255
- import * as path3 from "path";
3087
+ import * as path2 from "path";
3256
3088
 
3257
3089
  // src/lib/http-resource-method/http-resource-method-body.generator.ts
3258
3090
  var HttpResourceMethodBodyGenerator = class {
@@ -3492,17 +3324,32 @@ var HttpResourceMethodGenerator = class {
3492
3324
  parameters,
3493
3325
  returnType,
3494
3326
  statements: methodBody,
3495
- overloads
3327
+ overloads,
3328
+ docs: operation.description ? [
3329
+ operation.description
3330
+ ] : void 0
3496
3331
  });
3497
3332
  }
3498
3333
  generateMethodName(operation) {
3334
+ if (this.config.options.customizeMethodName) {
3335
+ if (operation.operationId == null) {
3336
+ throw new Error(`Operation ID is required for method name customization of operation: (${operation.method}) ${operation.path}`);
3337
+ }
3338
+ return this.config.options.customizeMethodName(operation.operationId);
3339
+ } else {
3340
+ return this.defaultNameGenerator(operation);
3341
+ }
3342
+ }
3343
+ defaultNameGenerator(operation) {
3499
3344
  if (operation.operationId) {
3500
3345
  return camelCase(operation.operationId);
3501
3346
  }
3502
- const method = operation.method.toLowerCase();
3503
- const pathParts = operation.path.split("/").filter((p) => p && !p.startsWith("{"));
3504
- const resource = pathParts[pathParts.length - 1] || "resource";
3505
- return `${method}${pascalCase(resource)}`;
3347
+ const method = pascalCase(operation.method.toLowerCase());
3348
+ const pathParts = operation.path.split("/").map((str2) => {
3349
+ return pascalCase(pascalCase(str2).replace(/[^a-zA-Z0-9]/g, ""));
3350
+ });
3351
+ const resource = pathParts.join("") || "resource";
3352
+ return `${camelCase(resource)}${method}`;
3506
3353
  }
3507
3354
  generateReturnType(operation) {
3508
3355
  const response = operation.responses?.["200"] || operation.responses?.["201"] || operation.responses?.["204"];
@@ -3545,8 +3392,8 @@ var HttpResourceMethodGenerator = class {
3545
3392
  };
3546
3393
 
3547
3394
  // src/lib/http-resource-index.generator.ts
3548
- import * as fs2 from "fs";
3549
- import * as path2 from "path";
3395
+ import * as fs from "fs";
3396
+ import * as path from "path";
3550
3397
  var HttpResourceIndexGenerator = class {
3551
3398
  static {
3552
3399
  __name(this, "HttpResourceIndexGenerator");
@@ -3556,13 +3403,13 @@ var HttpResourceIndexGenerator = class {
3556
3403
  this.project = project;
3557
3404
  }
3558
3405
  generateIndex(outputRoot) {
3559
- const servicesDir = path2.join(outputRoot, "resources");
3560
- const indexPath = path2.join(servicesDir, "index.ts");
3406
+ const servicesDir = path.join(outputRoot, "resources");
3407
+ const indexPath = path.join(servicesDir, "index.ts");
3561
3408
  const sourceFile = this.project.createSourceFile(indexPath, "", {
3562
3409
  overwrite: true
3563
3410
  });
3564
3411
  sourceFile.insertText(0, SERVICE_INDEX_GENERATOR_HEADER_COMMENT);
3565
- const serviceFiles = fs2.readdirSync(servicesDir).filter((file) => file.endsWith(".resource.ts")).map((file) => file.replace(".resource.ts", ""));
3412
+ const serviceFiles = fs.readdirSync(servicesDir).filter((file) => file.endsWith(".resource.ts")).map((file) => file.replace(".resource.ts", ""));
3566
3413
  serviceFiles.forEach((serviceName) => {
3567
3414
  const className = pascalCase(serviceName) + "Resource";
3568
3415
  sourceFile.addExportDeclaration({
@@ -3578,7 +3425,7 @@ var HttpResourceIndexGenerator = class {
3578
3425
  };
3579
3426
 
3580
3427
  // src/lib/http-resource.generator.ts
3581
- var HttpResourceGenerator = class _HttpResourceGenerator {
3428
+ var HttpResourceGenerator = class {
3582
3429
  static {
3583
3430
  __name(this, "HttpResourceGenerator");
3584
3431
  }
@@ -3600,12 +3447,8 @@ var HttpResourceGenerator = class _HttpResourceGenerator {
3600
3447
  }
3601
3448
  this.methodGenerator = new HttpResourceMethodGenerator(config);
3602
3449
  }
3603
- static async create(swaggerPathOrUrl, project, config) {
3604
- const parser = await SwaggerParser.create(swaggerPathOrUrl, config);
3605
- return new _HttpResourceGenerator(parser, project, config);
3606
- }
3607
- generate(outputRoot) {
3608
- const outputDir = path3.join(outputRoot, "resources");
3450
+ async generate(outputRoot) {
3451
+ const outputDir = path2.join(outputRoot, "resources");
3609
3452
  const paths = extractPaths(this.spec.paths, [
3610
3453
  "get"
3611
3454
  ]);
@@ -3614,19 +3457,19 @@ var HttpResourceGenerator = class _HttpResourceGenerator {
3614
3457
  return;
3615
3458
  }
3616
3459
  const controllerGroups = this.groupPathsByController(paths);
3617
- Object.entries(controllerGroups).forEach(([resourceName, operations]) => {
3460
+ await Promise.all(Object.entries(controllerGroups).map(([resourceName, operations]) => {
3618
3461
  this.generateServiceFile(resourceName, operations, outputDir);
3619
- });
3462
+ }));
3620
3463
  this.indexGenerator.generateIndex(outputRoot);
3621
3464
  }
3622
3465
  groupPathsByController(paths) {
3623
3466
  const groups = {};
3624
- paths.forEach((path4) => {
3467
+ paths.forEach((path3) => {
3625
3468
  let controllerName = "Default";
3626
- if (path4.tags && path4.tags.length > 0) {
3627
- controllerName = path4.tags[0];
3469
+ if (path3.tags && path3.tags.length > 0) {
3470
+ controllerName = path3.tags[0];
3628
3471
  } else {
3629
- const pathParts = path4.path.split("/").filter((p) => p && !p.startsWith("{"));
3472
+ const pathParts = path3.path.split("/").filter((p) => p && !p.startsWith("{"));
3630
3473
  if (pathParts.length > 1) {
3631
3474
  controllerName = pascalCase(pathParts[1]);
3632
3475
  }
@@ -3636,13 +3479,13 @@ var HttpResourceGenerator = class _HttpResourceGenerator {
3636
3479
  if (!groups[controllerName]) {
3637
3480
  groups[controllerName] = [];
3638
3481
  }
3639
- groups[controllerName].push(path4);
3482
+ groups[controllerName].push(path3);
3640
3483
  });
3641
3484
  return groups;
3642
3485
  }
3643
- generateServiceFile(resourceName, operations, outputDir) {
3486
+ async generateServiceFile(resourceName, operations, outputDir) {
3644
3487
  const fileName = `${camelCase(resourceName).replace(/Resource/, "")}.resource.ts`;
3645
- const filePath = path3.join(outputDir, fileName);
3488
+ const filePath = path2.join(outputDir, fileName);
3646
3489
  const sourceFile = this.project.createSourceFile(filePath, "", {
3647
3490
  overwrite: true
3648
3491
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-openapi/http-resource",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
4
4
  "description": "HTTP Resource plugin for ng-openapi - Angular HTTP utilities with caching and state management",
5
5
  "keywords": [
6
6
  "angular",