ng-openapi 0.0.38 → 0.0.40-pr-8-feature-url-support.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.cjs +268 -67
- package/index.d.ts +16 -2
- package/index.js +228 -52
- package/package.json +3 -2
package/cli.cjs
CHANGED
|
@@ -26,19 +26,25 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
|
|
27
27
|
// src/lib/cli.ts
|
|
28
28
|
var import_commander = require("commander");
|
|
29
|
-
var
|
|
29
|
+
var path10 = __toESM(require("path"));
|
|
30
30
|
var fs4 = __toESM(require("fs"));
|
|
31
31
|
|
|
32
32
|
// src/lib/core/swagger-parser.ts
|
|
33
33
|
var fs = __toESM(require("fs"));
|
|
34
|
-
var
|
|
34
|
+
var path = __toESM(require("path"));
|
|
35
|
+
var yaml = __toESM(require("js-yaml"));
|
|
36
|
+
var SwaggerParser = class _SwaggerParser {
|
|
35
37
|
static {
|
|
36
38
|
__name(this, "SwaggerParser");
|
|
37
39
|
}
|
|
38
40
|
spec;
|
|
39
|
-
constructor(
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
constructor(spec) {
|
|
42
|
+
this.spec = spec;
|
|
43
|
+
}
|
|
44
|
+
static async create(swaggerPathOrUrl) {
|
|
45
|
+
const swaggerContent = await _SwaggerParser.loadContent(swaggerPathOrUrl);
|
|
46
|
+
const spec = _SwaggerParser.parseSpecContent(swaggerContent, swaggerPathOrUrl);
|
|
47
|
+
return new _SwaggerParser(spec);
|
|
42
48
|
}
|
|
43
49
|
getDefinitions() {
|
|
44
50
|
return this.spec.definitions || this.spec.components?.schemas || {};
|
|
@@ -55,6 +61,125 @@ var SwaggerParser = class {
|
|
|
55
61
|
getAllDefinitionNames() {
|
|
56
62
|
return Object.keys(this.getDefinitions());
|
|
57
63
|
}
|
|
64
|
+
getSpec() {
|
|
65
|
+
return this.spec;
|
|
66
|
+
}
|
|
67
|
+
getPaths() {
|
|
68
|
+
return this.spec.paths || {};
|
|
69
|
+
}
|
|
70
|
+
isValidSpec() {
|
|
71
|
+
return !!(this.spec.swagger && this.spec.swagger.startsWith("2.") || this.spec.openapi && this.spec.openapi.startsWith("3."));
|
|
72
|
+
}
|
|
73
|
+
getSpecVersion() {
|
|
74
|
+
if (this.spec.swagger) {
|
|
75
|
+
return {
|
|
76
|
+
type: "swagger",
|
|
77
|
+
version: this.spec.swagger
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (this.spec.openapi) {
|
|
81
|
+
return {
|
|
82
|
+
type: "openapi",
|
|
83
|
+
version: this.spec.openapi
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
static async loadContent(pathOrUrl) {
|
|
89
|
+
if (_SwaggerParser.isUrl(pathOrUrl)) {
|
|
90
|
+
return await _SwaggerParser.fetchUrlContent(pathOrUrl);
|
|
91
|
+
} else {
|
|
92
|
+
return fs.readFileSync(pathOrUrl, "utf8");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
static isUrl(input) {
|
|
96
|
+
try {
|
|
97
|
+
new URL(input);
|
|
98
|
+
return true;
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
static async fetchUrlContent(url) {
|
|
104
|
+
try {
|
|
105
|
+
const response = await fetch(url, {
|
|
106
|
+
method: "GET",
|
|
107
|
+
headers: {
|
|
108
|
+
"Accept": "application/json, application/yaml, text/yaml, text/plain, */*",
|
|
109
|
+
"User-Agent": "ng-openapi"
|
|
110
|
+
},
|
|
111
|
+
// 30 second timeout
|
|
112
|
+
signal: AbortSignal.timeout(3e4)
|
|
113
|
+
});
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
116
|
+
}
|
|
117
|
+
const content = await response.text();
|
|
118
|
+
if (!content || content.trim() === "") {
|
|
119
|
+
throw new Error(`Empty response from URL: ${url}`);
|
|
120
|
+
}
|
|
121
|
+
return content;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
let errorMessage = `Failed to fetch content from URL: ${url}`;
|
|
124
|
+
if (error.name === "AbortError") {
|
|
125
|
+
errorMessage += " - Request timeout (30s)";
|
|
126
|
+
} else if (error.message) {
|
|
127
|
+
errorMessage += ` - ${error.message}`;
|
|
128
|
+
}
|
|
129
|
+
throw new Error(errorMessage);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
static parseSpecContent(content, pathOrUrl) {
|
|
133
|
+
let format;
|
|
134
|
+
if (_SwaggerParser.isUrl(pathOrUrl)) {
|
|
135
|
+
const urlPath = new URL(pathOrUrl).pathname.toLowerCase();
|
|
136
|
+
if (urlPath.endsWith(".json")) {
|
|
137
|
+
format = "json";
|
|
138
|
+
} else if (urlPath.endsWith(".yaml") || urlPath.endsWith(".yml")) {
|
|
139
|
+
format = "yaml";
|
|
140
|
+
} else {
|
|
141
|
+
format = _SwaggerParser.detectFormat(content);
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
const extension = path.extname(pathOrUrl).toLowerCase();
|
|
145
|
+
switch (extension) {
|
|
146
|
+
case ".json":
|
|
147
|
+
format = "json";
|
|
148
|
+
break;
|
|
149
|
+
case ".yaml":
|
|
150
|
+
format = "yaml";
|
|
151
|
+
break;
|
|
152
|
+
case ".yml":
|
|
153
|
+
format = "yml";
|
|
154
|
+
break;
|
|
155
|
+
default:
|
|
156
|
+
format = _SwaggerParser.detectFormat(content);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
switch (format) {
|
|
161
|
+
case "json":
|
|
162
|
+
return JSON.parse(content);
|
|
163
|
+
case "yaml":
|
|
164
|
+
case "yml":
|
|
165
|
+
return yaml.load(content);
|
|
166
|
+
default:
|
|
167
|
+
throw new Error(`Unable to determine format for: ${pathOrUrl}`);
|
|
168
|
+
}
|
|
169
|
+
} catch (error) {
|
|
170
|
+
throw new Error(`Failed to parse ${format.toUpperCase()} content from: ${pathOrUrl}. Error: ${error instanceof Error ? error.message : error}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
static detectFormat(content) {
|
|
174
|
+
const trimmed = content.trim();
|
|
175
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
176
|
+
return "json";
|
|
177
|
+
}
|
|
178
|
+
if (trimmed.includes("openapi:") || trimmed.includes("swagger:") || trimmed.includes("---") || /^[a-zA-Z][a-zA-Z0-9_]*\s*:/.test(trimmed)) {
|
|
179
|
+
return "yaml";
|
|
180
|
+
}
|
|
181
|
+
return "json";
|
|
182
|
+
}
|
|
58
183
|
};
|
|
59
184
|
|
|
60
185
|
// src/lib/core/generator.ts
|
|
@@ -99,7 +224,7 @@ var BASE_INTERCEPTOR_HEADER_COMMENT = /* @__PURE__ */ __name((clientName) => def
|
|
|
99
224
|
`, "BASE_INTERCEPTOR_HEADER_COMMENT");
|
|
100
225
|
|
|
101
226
|
// src/lib/generators/type/type.generator.ts
|
|
102
|
-
var TypeGenerator = class {
|
|
227
|
+
var TypeGenerator = class _TypeGenerator {
|
|
103
228
|
static {
|
|
104
229
|
__name(this, "TypeGenerator");
|
|
105
230
|
}
|
|
@@ -108,7 +233,7 @@ var TypeGenerator = class {
|
|
|
108
233
|
sourceFile;
|
|
109
234
|
generatedTypes = /* @__PURE__ */ new Set();
|
|
110
235
|
config;
|
|
111
|
-
constructor(
|
|
236
|
+
constructor(parser, outputRoot, config) {
|
|
112
237
|
this.config = config;
|
|
113
238
|
const outputPath = outputRoot + "/models/index.ts";
|
|
114
239
|
this.project = new import_ts_morph.Project({
|
|
@@ -120,15 +245,14 @@ var TypeGenerator = class {
|
|
|
120
245
|
...this.config.compilerOptions
|
|
121
246
|
}
|
|
122
247
|
});
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
248
|
+
this.parser = parser;
|
|
249
|
+
this.sourceFile = this.project.createSourceFile(outputPath, "", {
|
|
250
|
+
overwrite: true
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
static async create(swaggerPathOrUrl, outputRoot, config) {
|
|
254
|
+
const parser = await SwaggerParser.create(swaggerPathOrUrl);
|
|
255
|
+
return new _TypeGenerator(parser, outputRoot, config);
|
|
132
256
|
}
|
|
133
257
|
generate() {
|
|
134
258
|
try {
|
|
@@ -400,7 +524,7 @@ var TypeGenerator = class {
|
|
|
400
524
|
|
|
401
525
|
// src/lib/generators/utility/token.generator.ts
|
|
402
526
|
var import_ts_morph2 = require("ts-morph");
|
|
403
|
-
var
|
|
527
|
+
var path2 = __toESM(require("path"));
|
|
404
528
|
var TokenGenerator = class {
|
|
405
529
|
static {
|
|
406
530
|
__name(this, "TokenGenerator");
|
|
@@ -412,8 +536,8 @@ var TokenGenerator = class {
|
|
|
412
536
|
this.clientName = clientName;
|
|
413
537
|
}
|
|
414
538
|
generate(outputDir) {
|
|
415
|
-
const tokensDir =
|
|
416
|
-
const filePath =
|
|
539
|
+
const tokensDir = path2.join(outputDir, "tokens");
|
|
540
|
+
const filePath = path2.join(tokensDir, "index.ts");
|
|
417
541
|
const sourceFile = this.project.createSourceFile(filePath, "", {
|
|
418
542
|
overwrite: true
|
|
419
543
|
});
|
|
@@ -528,7 +652,7 @@ var TokenGenerator = class {
|
|
|
528
652
|
};
|
|
529
653
|
|
|
530
654
|
// src/lib/generators/utility/file-download.generator.ts
|
|
531
|
-
var
|
|
655
|
+
var path3 = __toESM(require("path"));
|
|
532
656
|
var FileDownloadGenerator = class {
|
|
533
657
|
static {
|
|
534
658
|
__name(this, "FileDownloadGenerator");
|
|
@@ -538,8 +662,8 @@ var FileDownloadGenerator = class {
|
|
|
538
662
|
this.project = project;
|
|
539
663
|
}
|
|
540
664
|
generate(outputDir) {
|
|
541
|
-
const utilsDir =
|
|
542
|
-
const filePath =
|
|
665
|
+
const utilsDir = path3.join(outputDir, "utils");
|
|
666
|
+
const filePath = path3.join(utilsDir, "file-download.ts");
|
|
543
667
|
const sourceFile = this.project.createSourceFile(filePath, "", {
|
|
544
668
|
overwrite: true
|
|
545
669
|
});
|
|
@@ -670,7 +794,7 @@ var FileDownloadGenerator = class {
|
|
|
670
794
|
|
|
671
795
|
// src/lib/generators/utility/date-transformer.generator.ts
|
|
672
796
|
var import_ts_morph3 = require("ts-morph");
|
|
673
|
-
var
|
|
797
|
+
var path4 = __toESM(require("path"));
|
|
674
798
|
var DateTransformerGenerator = class {
|
|
675
799
|
static {
|
|
676
800
|
__name(this, "DateTransformerGenerator");
|
|
@@ -680,8 +804,8 @@ var DateTransformerGenerator = class {
|
|
|
680
804
|
this.project = project;
|
|
681
805
|
}
|
|
682
806
|
generate(outputDir) {
|
|
683
|
-
const utilsDir =
|
|
684
|
-
const filePath =
|
|
807
|
+
const utilsDir = path4.join(outputDir, "utils");
|
|
808
|
+
const filePath = path4.join(utilsDir, "date-transformer.ts");
|
|
685
809
|
const sourceFile = this.project.createSourceFile(filePath, "", {
|
|
686
810
|
overwrite: true
|
|
687
811
|
});
|
|
@@ -804,7 +928,7 @@ var DateTransformerGenerator = class {
|
|
|
804
928
|
};
|
|
805
929
|
|
|
806
930
|
// src/lib/generators/utility/main-index.generator.ts
|
|
807
|
-
var
|
|
931
|
+
var path5 = __toESM(require("path"));
|
|
808
932
|
var MainIndexGenerator = class {
|
|
809
933
|
static {
|
|
810
934
|
__name(this, "MainIndexGenerator");
|
|
@@ -816,7 +940,7 @@ var MainIndexGenerator = class {
|
|
|
816
940
|
this.config = config;
|
|
817
941
|
}
|
|
818
942
|
generateMainIndex(outputRoot) {
|
|
819
|
-
const indexPath =
|
|
943
|
+
const indexPath = path5.join(outputRoot, "index.ts");
|
|
820
944
|
const sourceFile = this.project.createSourceFile(indexPath, "", {
|
|
821
945
|
overwrite: true
|
|
822
946
|
});
|
|
@@ -848,7 +972,7 @@ var MainIndexGenerator = class {
|
|
|
848
972
|
};
|
|
849
973
|
|
|
850
974
|
// src/lib/generators/utility/provider.generator.ts
|
|
851
|
-
var
|
|
975
|
+
var path6 = __toESM(require("path"));
|
|
852
976
|
var ProviderGenerator = class {
|
|
853
977
|
static {
|
|
854
978
|
__name(this, "ProviderGenerator");
|
|
@@ -862,7 +986,7 @@ var ProviderGenerator = class {
|
|
|
862
986
|
this.clientName = config.clientName || "default";
|
|
863
987
|
}
|
|
864
988
|
generate(outputDir) {
|
|
865
|
-
const filePath =
|
|
989
|
+
const filePath = path6.join(outputDir, "providers.ts");
|
|
866
990
|
const sourceFile = this.project.createSourceFile(filePath, "", {
|
|
867
991
|
overwrite: true
|
|
868
992
|
});
|
|
@@ -1055,7 +1179,7 @@ return makeEnvironmentProviders(providers);`;
|
|
|
1055
1179
|
|
|
1056
1180
|
// src/lib/generators/utility/base-interceptor.generator.ts
|
|
1057
1181
|
var import_ts_morph4 = require("ts-morph");
|
|
1058
|
-
var
|
|
1182
|
+
var path7 = __toESM(require("path"));
|
|
1059
1183
|
var BaseInterceptorGenerator = class {
|
|
1060
1184
|
static {
|
|
1061
1185
|
__name(this, "BaseInterceptorGenerator");
|
|
@@ -1067,8 +1191,8 @@ var BaseInterceptorGenerator = class {
|
|
|
1067
1191
|
this.#clientName = clientName;
|
|
1068
1192
|
}
|
|
1069
1193
|
generate(outputDir) {
|
|
1070
|
-
const utilsDir =
|
|
1071
|
-
const filePath =
|
|
1194
|
+
const utilsDir = path7.join(outputDir, "utils");
|
|
1195
|
+
const filePath = path7.join(utilsDir, "base-interceptor.ts");
|
|
1072
1196
|
const sourceFile = this.#project.createSourceFile(filePath, "", {
|
|
1073
1197
|
overwrite: true
|
|
1074
1198
|
});
|
|
@@ -1187,7 +1311,7 @@ var BaseInterceptorGenerator = class {
|
|
|
1187
1311
|
|
|
1188
1312
|
// src/lib/generators/service/service.generator.ts
|
|
1189
1313
|
var import_ts_morph5 = require("ts-morph");
|
|
1190
|
-
var
|
|
1314
|
+
var path8 = __toESM(require("path"));
|
|
1191
1315
|
|
|
1192
1316
|
// src/lib/utils/string.utils.ts
|
|
1193
1317
|
function camelCase(str) {
|
|
@@ -1970,7 +2094,7 @@ var ServiceMethodGenerator = class {
|
|
|
1970
2094
|
};
|
|
1971
2095
|
|
|
1972
2096
|
// src/lib/generators/service/service.generator.ts
|
|
1973
|
-
var ServiceGenerator = class {
|
|
2097
|
+
var ServiceGenerator = class _ServiceGenerator {
|
|
1974
2098
|
static {
|
|
1975
2099
|
__name(this, "ServiceGenerator");
|
|
1976
2100
|
}
|
|
@@ -1979,16 +2103,28 @@ var ServiceGenerator = class {
|
|
|
1979
2103
|
spec;
|
|
1980
2104
|
config;
|
|
1981
2105
|
methodGenerator;
|
|
1982
|
-
constructor(
|
|
2106
|
+
constructor(parser, project, config) {
|
|
1983
2107
|
this.config = config;
|
|
1984
2108
|
this.project = project;
|
|
1985
|
-
this.parser =
|
|
1986
|
-
this.spec =
|
|
2109
|
+
this.parser = parser;
|
|
2110
|
+
this.spec = this.parser.getSpec();
|
|
2111
|
+
if (!this.parser.isValidSpec()) {
|
|
2112
|
+
const versionInfo = this.parser.getSpecVersion();
|
|
2113
|
+
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"}`);
|
|
2114
|
+
}
|
|
1987
2115
|
this.methodGenerator = new ServiceMethodGenerator(config);
|
|
1988
2116
|
}
|
|
2117
|
+
static async create(swaggerPathOrUrl, project, config) {
|
|
2118
|
+
const parser = await SwaggerParser.create(swaggerPathOrUrl);
|
|
2119
|
+
return new _ServiceGenerator(parser, project, config);
|
|
2120
|
+
}
|
|
1989
2121
|
generate(outputRoot) {
|
|
1990
|
-
const outputDir =
|
|
2122
|
+
const outputDir = path8.join(outputRoot, "services");
|
|
1991
2123
|
const paths = this.extractPaths();
|
|
2124
|
+
if (paths.length === 0) {
|
|
2125
|
+
console.warn("No API paths found in the specification");
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
1992
2128
|
const controllerGroups = this.groupPathsByController(paths);
|
|
1993
2129
|
Object.entries(controllerGroups).forEach(([controllerName, operations]) => {
|
|
1994
2130
|
this.generateServiceFile(controllerName, operations, outputDir);
|
|
@@ -1997,7 +2133,7 @@ var ServiceGenerator = class {
|
|
|
1997
2133
|
extractPaths() {
|
|
1998
2134
|
const paths = [];
|
|
1999
2135
|
const swaggerPaths = this.spec.paths || {};
|
|
2000
|
-
Object.entries(swaggerPaths).forEach(([
|
|
2136
|
+
Object.entries(swaggerPaths).forEach(([path11, pathItem]) => {
|
|
2001
2137
|
const methods = [
|
|
2002
2138
|
"get",
|
|
2003
2139
|
"post",
|
|
@@ -2011,7 +2147,7 @@ var ServiceGenerator = class {
|
|
|
2011
2147
|
if (pathItem[method]) {
|
|
2012
2148
|
const operation = pathItem[method];
|
|
2013
2149
|
paths.push({
|
|
2014
|
-
path:
|
|
2150
|
+
path: path11,
|
|
2015
2151
|
method: method.toUpperCase(),
|
|
2016
2152
|
operationId: operation.operationId,
|
|
2017
2153
|
summary: operation.summary,
|
|
@@ -2043,12 +2179,12 @@ var ServiceGenerator = class {
|
|
|
2043
2179
|
}
|
|
2044
2180
|
groupPathsByController(paths) {
|
|
2045
2181
|
const groups = {};
|
|
2046
|
-
paths.forEach((
|
|
2182
|
+
paths.forEach((path11) => {
|
|
2047
2183
|
let controllerName = "Default";
|
|
2048
|
-
if (
|
|
2049
|
-
controllerName =
|
|
2184
|
+
if (path11.tags && path11.tags.length > 0) {
|
|
2185
|
+
controllerName = path11.tags[0];
|
|
2050
2186
|
} else {
|
|
2051
|
-
const pathParts =
|
|
2187
|
+
const pathParts = path11.path.split("/").filter((p) => p && !p.startsWith("{"));
|
|
2052
2188
|
if (pathParts.length > 1) {
|
|
2053
2189
|
controllerName = pascalCase(pathParts[1]);
|
|
2054
2190
|
}
|
|
@@ -2057,13 +2193,13 @@ var ServiceGenerator = class {
|
|
|
2057
2193
|
if (!groups[controllerName]) {
|
|
2058
2194
|
groups[controllerName] = [];
|
|
2059
2195
|
}
|
|
2060
|
-
groups[controllerName].push(
|
|
2196
|
+
groups[controllerName].push(path11);
|
|
2061
2197
|
});
|
|
2062
2198
|
return groups;
|
|
2063
2199
|
}
|
|
2064
2200
|
generateServiceFile(controllerName, operations, outputDir) {
|
|
2065
2201
|
const fileName = `${camelCase(controllerName)}.service.ts`;
|
|
2066
|
-
const filePath =
|
|
2202
|
+
const filePath = path8.join(outputDir, fileName);
|
|
2067
2203
|
const sourceFile = this.project.createSourceFile(filePath, "", {
|
|
2068
2204
|
overwrite: true
|
|
2069
2205
|
});
|
|
@@ -2257,7 +2393,7 @@ return context.set(this.clientContextToken, '${this.config.clientName || "defaul
|
|
|
2257
2393
|
|
|
2258
2394
|
// src/lib/generators/service/service-index.generator.ts
|
|
2259
2395
|
var fs2 = __toESM(require("fs"));
|
|
2260
|
-
var
|
|
2396
|
+
var path9 = __toESM(require("path"));
|
|
2261
2397
|
var ServiceIndexGenerator = class {
|
|
2262
2398
|
static {
|
|
2263
2399
|
__name(this, "ServiceIndexGenerator");
|
|
@@ -2267,8 +2403,8 @@ var ServiceIndexGenerator = class {
|
|
|
2267
2403
|
this.project = project;
|
|
2268
2404
|
}
|
|
2269
2405
|
generateIndex(outputRoot) {
|
|
2270
|
-
const servicesDir =
|
|
2271
|
-
const indexPath =
|
|
2406
|
+
const servicesDir = path9.join(outputRoot, "services");
|
|
2407
|
+
const indexPath = path9.join(servicesDir, "index.ts");
|
|
2272
2408
|
const sourceFile = this.project.createSourceFile(indexPath, "", {
|
|
2273
2409
|
overwrite: true
|
|
2274
2410
|
});
|
|
@@ -2289,12 +2425,36 @@ var ServiceIndexGenerator = class {
|
|
|
2289
2425
|
|
|
2290
2426
|
// src/lib/core/generator.ts
|
|
2291
2427
|
var fs3 = __toESM(require("fs"));
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2428
|
+
function isUrl(input) {
|
|
2429
|
+
try {
|
|
2430
|
+
new URL(input);
|
|
2431
|
+
return true;
|
|
2432
|
+
} catch {
|
|
2433
|
+
return false;
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
__name(isUrl, "isUrl");
|
|
2437
|
+
function validateInput(input) {
|
|
2438
|
+
if (isUrl(input)) {
|
|
2439
|
+
const url = new URL(input);
|
|
2440
|
+
if (![
|
|
2441
|
+
"http:",
|
|
2442
|
+
"https:"
|
|
2443
|
+
].includes(url.protocol)) {
|
|
2444
|
+
throw new Error(`Unsupported URL protocol: ${url.protocol}. Only HTTP and HTTPS are supported.`);
|
|
2445
|
+
}
|
|
2446
|
+
} else {
|
|
2447
|
+
if (!fs3.existsSync(input)) {
|
|
2448
|
+
throw new Error(`Input file not found: ${input}`);
|
|
2449
|
+
}
|
|
2295
2450
|
}
|
|
2451
|
+
}
|
|
2452
|
+
__name(validateInput, "validateInput");
|
|
2453
|
+
async function generateFromConfig(config) {
|
|
2454
|
+
validateInput(config.input);
|
|
2296
2455
|
const outputPath = config.output;
|
|
2297
2456
|
const generateServices = config.options.generateServices ?? true;
|
|
2457
|
+
const inputType = isUrl(config.input) ? "URL" : "file";
|
|
2298
2458
|
if (!fs3.existsSync(outputPath)) {
|
|
2299
2459
|
fs3.mkdirSync(outputPath, {
|
|
2300
2460
|
recursive: true
|
|
@@ -2310,7 +2470,8 @@ async function generateFromConfig(config) {
|
|
|
2310
2470
|
...config.compilerOptions
|
|
2311
2471
|
}
|
|
2312
2472
|
});
|
|
2313
|
-
|
|
2473
|
+
console.log(`\u{1F4E1} Processing OpenAPI specification from ${inputType}: ${config.input}`);
|
|
2474
|
+
const typeGenerator = await TypeGenerator.create(config.input, outputPath, config);
|
|
2314
2475
|
typeGenerator.generate();
|
|
2315
2476
|
console.log(`\u2705 TypeScript interfaces generated`);
|
|
2316
2477
|
if (generateServices) {
|
|
@@ -2322,7 +2483,7 @@ async function generateFromConfig(config) {
|
|
|
2322
2483
|
}
|
|
2323
2484
|
const fileDownloadHelper = new FileDownloadGenerator(project);
|
|
2324
2485
|
fileDownloadHelper.generate(outputPath);
|
|
2325
|
-
const serviceGenerator =
|
|
2486
|
+
const serviceGenerator = await ServiceGenerator.create(config.input, project, config);
|
|
2326
2487
|
serviceGenerator.generate(outputPath);
|
|
2327
2488
|
const indexGenerator = new ServiceIndexGenerator(project);
|
|
2328
2489
|
indexGenerator.generateIndex(outputPath);
|
|
@@ -2334,14 +2495,19 @@ async function generateFromConfig(config) {
|
|
|
2334
2495
|
}
|
|
2335
2496
|
const mainIndexGenerator = new MainIndexGenerator(project, config);
|
|
2336
2497
|
mainIndexGenerator.generateMainIndex(outputPath);
|
|
2498
|
+
const sourceInfo = `from ${inputType}: ${config.input}`;
|
|
2337
2499
|
if (config.clientName) {
|
|
2338
|
-
console.log(`\u{1F389} ${config.clientName} Generation completed successfully
|
|
2500
|
+
console.log(`\u{1F389} ${config.clientName} Generation completed successfully ${sourceInfo} -> ${outputPath}`);
|
|
2339
2501
|
} else {
|
|
2340
|
-
console.log(
|
|
2502
|
+
console.log(`\u{1F389} Generation completed successfully ${sourceInfo} -> ${outputPath}`);
|
|
2341
2503
|
}
|
|
2342
2504
|
} catch (error) {
|
|
2343
2505
|
if (error instanceof Error) {
|
|
2344
2506
|
console.error("\u274C Error during generation:", error.message);
|
|
2507
|
+
if (error.message.includes("fetch") || error.message.includes("Failed to fetch")) {
|
|
2508
|
+
console.error("\u{1F4A1} Tip: Make sure the URL is accessible and returns a valid OpenAPI/Swagger specification");
|
|
2509
|
+
console.error("\u{1F4A1} Alternative: Download the specification file locally and use the file path instead");
|
|
2510
|
+
}
|
|
2345
2511
|
} else {
|
|
2346
2512
|
console.error("\u274C Unknown error during generation:", error);
|
|
2347
2513
|
}
|
|
@@ -2351,12 +2517,12 @@ async function generateFromConfig(config) {
|
|
|
2351
2517
|
__name(generateFromConfig, "generateFromConfig");
|
|
2352
2518
|
|
|
2353
2519
|
// package.json
|
|
2354
|
-
var version = "0.0.
|
|
2520
|
+
var version = "0.0.39";
|
|
2355
2521
|
|
|
2356
2522
|
// src/lib/cli.ts
|
|
2357
2523
|
var program = new import_commander.Command();
|
|
2358
2524
|
async function loadConfigFile(configPath) {
|
|
2359
|
-
const resolvedPath =
|
|
2525
|
+
const resolvedPath = path10.resolve(configPath);
|
|
2360
2526
|
if (!fs4.existsSync(resolvedPath)) {
|
|
2361
2527
|
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
2362
2528
|
}
|
|
@@ -2376,19 +2542,50 @@ async function loadConfigFile(configPath) {
|
|
|
2376
2542
|
}
|
|
2377
2543
|
}
|
|
2378
2544
|
__name(loadConfigFile, "loadConfigFile");
|
|
2545
|
+
function isUrl2(input) {
|
|
2546
|
+
try {
|
|
2547
|
+
new URL(input);
|
|
2548
|
+
return true;
|
|
2549
|
+
} catch {
|
|
2550
|
+
return false;
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
__name(isUrl2, "isUrl");
|
|
2554
|
+
function validateInput2(inputPath) {
|
|
2555
|
+
if (isUrl2(inputPath)) {
|
|
2556
|
+
const url = new URL(inputPath);
|
|
2557
|
+
if (![
|
|
2558
|
+
"http:",
|
|
2559
|
+
"https:"
|
|
2560
|
+
].includes(url.protocol)) {
|
|
2561
|
+
throw new Error(`Unsupported URL protocol: ${url.protocol}. Only HTTP and HTTPS are supported.`);
|
|
2562
|
+
}
|
|
2563
|
+
return;
|
|
2564
|
+
}
|
|
2565
|
+
if (!fs4.existsSync(inputPath)) {
|
|
2566
|
+
throw new Error(`Input file not found: ${inputPath}`);
|
|
2567
|
+
}
|
|
2568
|
+
const extension = path10.extname(inputPath).toLowerCase();
|
|
2569
|
+
const supportedExtensions = [
|
|
2570
|
+
".json",
|
|
2571
|
+
".yaml",
|
|
2572
|
+
".yml"
|
|
2573
|
+
];
|
|
2574
|
+
if (!supportedExtensions.includes(extension)) {
|
|
2575
|
+
throw new Error(`Failed to parse ${extension || "specification"}. Supported formats are .json, .yaml, and .yml.`);
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
__name(validateInput2, "validateInput");
|
|
2379
2579
|
async function generateFromOptions(options) {
|
|
2380
2580
|
try {
|
|
2381
2581
|
if (options.config) {
|
|
2382
2582
|
const config = await loadConfigFile(options.config);
|
|
2583
|
+
validateInput2(config.input);
|
|
2383
2584
|
await generateFromConfig(config);
|
|
2384
2585
|
} else if (options.input) {
|
|
2385
|
-
|
|
2386
|
-
if (!fs4.existsSync(inputPath)) {
|
|
2387
|
-
console.error(`Error: Input file not found: ${inputPath}`);
|
|
2388
|
-
process.exit(1);
|
|
2389
|
-
}
|
|
2586
|
+
validateInput2(options.input);
|
|
2390
2587
|
const config = {
|
|
2391
|
-
input:
|
|
2588
|
+
input: options.input,
|
|
2392
2589
|
output: options.output || "./src/generated",
|
|
2393
2590
|
options: {
|
|
2394
2591
|
dateType: options.dateType || "Date",
|
|
@@ -2410,10 +2607,10 @@ async function generateFromOptions(options) {
|
|
|
2410
2607
|
}
|
|
2411
2608
|
}
|
|
2412
2609
|
__name(generateFromOptions, "generateFromOptions");
|
|
2413
|
-
program.name("ng-openapi").description("Generate Angular services and types from Swagger
|
|
2610
|
+
program.name("ng-openapi").description("Generate Angular services and types from OpenAPI/Swagger specifications (JSON, YAML, YML) from files or URLs").version(version).option("-c, --config <path>", "Path to configuration file").option("-i, --input <path>", "Path or URL to OpenAPI/Swagger specification (.json, .yaml, .yml)").option("-o, --output <path>", "Output directory", "./src/generated").option("--types-only", "Generate only TypeScript interfaces").option("--date-type <type>", "Date type to use (string | Date)", "Date").action(async (options) => {
|
|
2414
2611
|
await generateFromOptions(options);
|
|
2415
2612
|
});
|
|
2416
|
-
program.command("generate").alias("gen").description("Generate code from Swagger specification").option("-c, --config <path>", "Path to configuration file").option("-i, --input <path>", "Path to Swagger
|
|
2613
|
+
program.command("generate").alias("gen").description("Generate code from OpenAPI/Swagger specification").option("-c, --config <path>", "Path to configuration file").option("-i, --input <path>", "Path or URL to OpenAPI/Swagger specification (.json, .yaml, .yml)").option("-o, --output <path>", "Output directory", "./src/generated").option("--types-only", "Generate only TypeScript interfaces").option("--date-type <type>", "Date type to use (string | Date)", "Date").action(async (options) => {
|
|
2417
2614
|
await generateFromOptions(options);
|
|
2418
2615
|
});
|
|
2419
2616
|
program.on("--help", () => {
|
|
@@ -2421,8 +2618,12 @@ program.on("--help", () => {
|
|
|
2421
2618
|
console.log("Examples:");
|
|
2422
2619
|
console.log(" $ ng-openapi -c ./openapi.config.ts");
|
|
2423
2620
|
console.log(" $ ng-openapi -i ./swagger.json -o ./src/api");
|
|
2621
|
+
console.log(" $ ng-openapi -i ./openapi.yaml -o ./src/api");
|
|
2622
|
+
console.log(" $ ng-openapi -i ./api-spec.yml -o ./src/api");
|
|
2623
|
+
console.log(" $ ng-openapi -i https://api.example.com/openapi.json -o ./src/api");
|
|
2624
|
+
console.log(" $ ng-openapi -i https://petstore.swagger.io/v2/swagger.json -o ./src/api");
|
|
2424
2625
|
console.log(" $ ng-openapi generate -c ./openapi.config.ts");
|
|
2425
|
-
console.log(" $ ng-openapi generate -i
|
|
2626
|
+
console.log(" $ ng-openapi generate -i https://api.example.com/swagger.yaml --types-only");
|
|
2426
2627
|
});
|
|
2427
2628
|
program.parse();
|
|
2428
2629
|
//# sourceMappingURL=cli.cjs.map
|
package/index.d.ts
CHANGED
|
@@ -125,6 +125,7 @@ interface SwaggerDefinition {
|
|
|
125
125
|
anyOf?: SwaggerDefinition[];
|
|
126
126
|
}
|
|
127
127
|
interface SwaggerSpec {
|
|
128
|
+
openapi: string;
|
|
128
129
|
swagger: string;
|
|
129
130
|
info: Info;
|
|
130
131
|
externalDocs?: ExternalDocs | undefined;
|
|
@@ -162,12 +163,25 @@ type EnumValueObject = {
|
|
|
162
163
|
};
|
|
163
164
|
|
|
164
165
|
declare class SwaggerParser {
|
|
165
|
-
private spec;
|
|
166
|
-
constructor(
|
|
166
|
+
private readonly spec;
|
|
167
|
+
private constructor();
|
|
168
|
+
static create(swaggerPathOrUrl: string): Promise<SwaggerParser>;
|
|
167
169
|
getDefinitions(): Record<string, SwaggerDefinition>;
|
|
168
170
|
getDefinition(name: string): SwaggerDefinition | undefined;
|
|
169
171
|
resolveReference(ref: string): SwaggerDefinition | undefined;
|
|
170
172
|
getAllDefinitionNames(): string[];
|
|
173
|
+
getSpec(): SwaggerSpec;
|
|
174
|
+
getPaths(): Record<string, any>;
|
|
175
|
+
isValidSpec(): boolean;
|
|
176
|
+
getSpecVersion(): {
|
|
177
|
+
type: "swagger" | "openapi";
|
|
178
|
+
version: string;
|
|
179
|
+
} | null;
|
|
180
|
+
private static loadContent;
|
|
181
|
+
private static isUrl;
|
|
182
|
+
private static fetchUrlContent;
|
|
183
|
+
private static parseSpecContent;
|
|
184
|
+
private static detectFormat;
|
|
171
185
|
}
|
|
172
186
|
|
|
173
187
|
/**
|
package/index.js
CHANGED
|
@@ -80,11 +80,19 @@ module.exports = __toCommonJS(index_exports);
|
|
|
80
80
|
|
|
81
81
|
// src/lib/core/swagger-parser.ts
|
|
82
82
|
var fs = __toESM(require("fs"));
|
|
83
|
+
var path = __toESM(require("path"));
|
|
84
|
+
var yaml = __toESM(require("js-yaml"));
|
|
83
85
|
var _SwaggerParser = class _SwaggerParser {
|
|
84
|
-
constructor(
|
|
86
|
+
constructor(spec) {
|
|
85
87
|
__publicField(this, "spec");
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
this.spec = spec;
|
|
89
|
+
}
|
|
90
|
+
static create(swaggerPathOrUrl) {
|
|
91
|
+
return __async(this, null, function* () {
|
|
92
|
+
const swaggerContent = yield _SwaggerParser.loadContent(swaggerPathOrUrl);
|
|
93
|
+
const spec = _SwaggerParser.parseSpecContent(swaggerContent, swaggerPathOrUrl);
|
|
94
|
+
return new _SwaggerParser(spec);
|
|
95
|
+
});
|
|
88
96
|
}
|
|
89
97
|
getDefinitions() {
|
|
90
98
|
var _a;
|
|
@@ -102,6 +110,129 @@ var _SwaggerParser = class _SwaggerParser {
|
|
|
102
110
|
getAllDefinitionNames() {
|
|
103
111
|
return Object.keys(this.getDefinitions());
|
|
104
112
|
}
|
|
113
|
+
getSpec() {
|
|
114
|
+
return this.spec;
|
|
115
|
+
}
|
|
116
|
+
getPaths() {
|
|
117
|
+
return this.spec.paths || {};
|
|
118
|
+
}
|
|
119
|
+
isValidSpec() {
|
|
120
|
+
return !!(this.spec.swagger && this.spec.swagger.startsWith("2.") || this.spec.openapi && this.spec.openapi.startsWith("3."));
|
|
121
|
+
}
|
|
122
|
+
getSpecVersion() {
|
|
123
|
+
if (this.spec.swagger) {
|
|
124
|
+
return {
|
|
125
|
+
type: "swagger",
|
|
126
|
+
version: this.spec.swagger
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
if (this.spec.openapi) {
|
|
130
|
+
return {
|
|
131
|
+
type: "openapi",
|
|
132
|
+
version: this.spec.openapi
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
static loadContent(pathOrUrl) {
|
|
138
|
+
return __async(this, null, function* () {
|
|
139
|
+
if (_SwaggerParser.isUrl(pathOrUrl)) {
|
|
140
|
+
return yield _SwaggerParser.fetchUrlContent(pathOrUrl);
|
|
141
|
+
} else {
|
|
142
|
+
return fs.readFileSync(pathOrUrl, "utf8");
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
static isUrl(input) {
|
|
147
|
+
try {
|
|
148
|
+
new URL(input);
|
|
149
|
+
return true;
|
|
150
|
+
} catch (e) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
static fetchUrlContent(url) {
|
|
155
|
+
return __async(this, null, function* () {
|
|
156
|
+
try {
|
|
157
|
+
const response = yield fetch(url, {
|
|
158
|
+
method: "GET",
|
|
159
|
+
headers: {
|
|
160
|
+
"Accept": "application/json, application/yaml, text/yaml, text/plain, */*",
|
|
161
|
+
"User-Agent": "ng-openapi"
|
|
162
|
+
},
|
|
163
|
+
// 30 second timeout
|
|
164
|
+
signal: AbortSignal.timeout(3e4)
|
|
165
|
+
});
|
|
166
|
+
if (!response.ok) {
|
|
167
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
168
|
+
}
|
|
169
|
+
const content = yield response.text();
|
|
170
|
+
if (!content || content.trim() === "") {
|
|
171
|
+
throw new Error(`Empty response from URL: ${url}`);
|
|
172
|
+
}
|
|
173
|
+
return content;
|
|
174
|
+
} catch (error) {
|
|
175
|
+
let errorMessage = `Failed to fetch content from URL: ${url}`;
|
|
176
|
+
if (error.name === "AbortError") {
|
|
177
|
+
errorMessage += " - Request timeout (30s)";
|
|
178
|
+
} else if (error.message) {
|
|
179
|
+
errorMessage += ` - ${error.message}`;
|
|
180
|
+
}
|
|
181
|
+
throw new Error(errorMessage);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
static parseSpecContent(content, pathOrUrl) {
|
|
186
|
+
let format;
|
|
187
|
+
if (_SwaggerParser.isUrl(pathOrUrl)) {
|
|
188
|
+
const urlPath = new URL(pathOrUrl).pathname.toLowerCase();
|
|
189
|
+
if (urlPath.endsWith(".json")) {
|
|
190
|
+
format = "json";
|
|
191
|
+
} else if (urlPath.endsWith(".yaml") || urlPath.endsWith(".yml")) {
|
|
192
|
+
format = "yaml";
|
|
193
|
+
} else {
|
|
194
|
+
format = _SwaggerParser.detectFormat(content);
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
const extension = path.extname(pathOrUrl).toLowerCase();
|
|
198
|
+
switch (extension) {
|
|
199
|
+
case ".json":
|
|
200
|
+
format = "json";
|
|
201
|
+
break;
|
|
202
|
+
case ".yaml":
|
|
203
|
+
format = "yaml";
|
|
204
|
+
break;
|
|
205
|
+
case ".yml":
|
|
206
|
+
format = "yml";
|
|
207
|
+
break;
|
|
208
|
+
default:
|
|
209
|
+
format = _SwaggerParser.detectFormat(content);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
switch (format) {
|
|
214
|
+
case "json":
|
|
215
|
+
return JSON.parse(content);
|
|
216
|
+
case "yaml":
|
|
217
|
+
case "yml":
|
|
218
|
+
return yaml.load(content);
|
|
219
|
+
default:
|
|
220
|
+
throw new Error(`Unable to determine format for: ${pathOrUrl}`);
|
|
221
|
+
}
|
|
222
|
+
} catch (error) {
|
|
223
|
+
throw new Error(`Failed to parse ${format.toUpperCase()} content from: ${pathOrUrl}. Error: ${error instanceof Error ? error.message : error}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
static detectFormat(content) {
|
|
227
|
+
const trimmed = content.trim();
|
|
228
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
229
|
+
return "json";
|
|
230
|
+
}
|
|
231
|
+
if (trimmed.includes("openapi:") || trimmed.includes("swagger:") || trimmed.includes("---") || /^[a-zA-Z][a-zA-Z0-9_]*\s*:/.test(trimmed)) {
|
|
232
|
+
return "yaml";
|
|
233
|
+
}
|
|
234
|
+
return "json";
|
|
235
|
+
}
|
|
105
236
|
};
|
|
106
237
|
__name(_SwaggerParser, "SwaggerParser");
|
|
107
238
|
var SwaggerParser = _SwaggerParser;
|
|
@@ -149,7 +280,7 @@ var BASE_INTERCEPTOR_HEADER_COMMENT = /* @__PURE__ */ __name((clientName) => def
|
|
|
149
280
|
|
|
150
281
|
// src/lib/generators/type/type.generator.ts
|
|
151
282
|
var _TypeGenerator = class _TypeGenerator {
|
|
152
|
-
constructor(
|
|
283
|
+
constructor(parser, outputRoot, config) {
|
|
153
284
|
__publicField(this, "project");
|
|
154
285
|
__publicField(this, "parser");
|
|
155
286
|
__publicField(this, "sourceFile");
|
|
@@ -165,15 +296,16 @@ var _TypeGenerator = class _TypeGenerator {
|
|
|
165
296
|
strict: true
|
|
166
297
|
}, this.config.compilerOptions)
|
|
167
298
|
});
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
299
|
+
this.parser = parser;
|
|
300
|
+
this.sourceFile = this.project.createSourceFile(outputPath, "", {
|
|
301
|
+
overwrite: true
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
static create(swaggerPathOrUrl, outputRoot, config) {
|
|
305
|
+
return __async(this, null, function* () {
|
|
306
|
+
const parser = yield SwaggerParser.create(swaggerPathOrUrl);
|
|
307
|
+
return new _TypeGenerator(parser, outputRoot, config);
|
|
308
|
+
});
|
|
177
309
|
}
|
|
178
310
|
generate() {
|
|
179
311
|
try {
|
|
@@ -450,7 +582,7 @@ var TypeGenerator = _TypeGenerator;
|
|
|
450
582
|
|
|
451
583
|
// src/lib/generators/utility/token.generator.ts
|
|
452
584
|
var import_ts_morph2 = require("ts-morph");
|
|
453
|
-
var
|
|
585
|
+
var path2 = __toESM(require("path"));
|
|
454
586
|
var _TokenGenerator = class _TokenGenerator {
|
|
455
587
|
constructor(project, clientName = "default") {
|
|
456
588
|
__publicField(this, "project");
|
|
@@ -459,8 +591,8 @@ var _TokenGenerator = class _TokenGenerator {
|
|
|
459
591
|
this.clientName = clientName;
|
|
460
592
|
}
|
|
461
593
|
generate(outputDir) {
|
|
462
|
-
const tokensDir =
|
|
463
|
-
const filePath =
|
|
594
|
+
const tokensDir = path2.join(outputDir, "tokens");
|
|
595
|
+
const filePath = path2.join(tokensDir, "index.ts");
|
|
464
596
|
const sourceFile = this.project.createSourceFile(filePath, "", {
|
|
465
597
|
overwrite: true
|
|
466
598
|
});
|
|
@@ -577,15 +709,15 @@ __name(_TokenGenerator, "TokenGenerator");
|
|
|
577
709
|
var TokenGenerator = _TokenGenerator;
|
|
578
710
|
|
|
579
711
|
// src/lib/generators/utility/file-download.generator.ts
|
|
580
|
-
var
|
|
712
|
+
var path3 = __toESM(require("path"));
|
|
581
713
|
var _FileDownloadGenerator = class _FileDownloadGenerator {
|
|
582
714
|
constructor(project) {
|
|
583
715
|
__publicField(this, "project");
|
|
584
716
|
this.project = project;
|
|
585
717
|
}
|
|
586
718
|
generate(outputDir) {
|
|
587
|
-
const utilsDir =
|
|
588
|
-
const filePath =
|
|
719
|
+
const utilsDir = path3.join(outputDir, "utils");
|
|
720
|
+
const filePath = path3.join(utilsDir, "file-download.ts");
|
|
589
721
|
const sourceFile = this.project.createSourceFile(filePath, "", {
|
|
590
722
|
overwrite: true
|
|
591
723
|
});
|
|
@@ -718,15 +850,15 @@ var FileDownloadGenerator = _FileDownloadGenerator;
|
|
|
718
850
|
|
|
719
851
|
// src/lib/generators/utility/date-transformer.generator.ts
|
|
720
852
|
var import_ts_morph3 = require("ts-morph");
|
|
721
|
-
var
|
|
853
|
+
var path4 = __toESM(require("path"));
|
|
722
854
|
var _DateTransformerGenerator = class _DateTransformerGenerator {
|
|
723
855
|
constructor(project) {
|
|
724
856
|
__publicField(this, "project");
|
|
725
857
|
this.project = project;
|
|
726
858
|
}
|
|
727
859
|
generate(outputDir) {
|
|
728
|
-
const utilsDir =
|
|
729
|
-
const filePath =
|
|
860
|
+
const utilsDir = path4.join(outputDir, "utils");
|
|
861
|
+
const filePath = path4.join(utilsDir, "date-transformer.ts");
|
|
730
862
|
const sourceFile = this.project.createSourceFile(filePath, "", {
|
|
731
863
|
overwrite: true
|
|
732
864
|
});
|
|
@@ -851,7 +983,7 @@ __name(_DateTransformerGenerator, "DateTransformerGenerator");
|
|
|
851
983
|
var DateTransformerGenerator = _DateTransformerGenerator;
|
|
852
984
|
|
|
853
985
|
// src/lib/generators/utility/main-index.generator.ts
|
|
854
|
-
var
|
|
986
|
+
var path5 = __toESM(require("path"));
|
|
855
987
|
var _MainIndexGenerator = class _MainIndexGenerator {
|
|
856
988
|
constructor(project, config) {
|
|
857
989
|
__publicField(this, "project");
|
|
@@ -860,7 +992,7 @@ var _MainIndexGenerator = class _MainIndexGenerator {
|
|
|
860
992
|
this.config = config;
|
|
861
993
|
}
|
|
862
994
|
generateMainIndex(outputRoot) {
|
|
863
|
-
const indexPath =
|
|
995
|
+
const indexPath = path5.join(outputRoot, "index.ts");
|
|
864
996
|
const sourceFile = this.project.createSourceFile(indexPath, "", {
|
|
865
997
|
overwrite: true
|
|
866
998
|
});
|
|
@@ -894,7 +1026,7 @@ __name(_MainIndexGenerator, "MainIndexGenerator");
|
|
|
894
1026
|
var MainIndexGenerator = _MainIndexGenerator;
|
|
895
1027
|
|
|
896
1028
|
// src/lib/generators/utility/provider.generator.ts
|
|
897
|
-
var
|
|
1029
|
+
var path6 = __toESM(require("path"));
|
|
898
1030
|
var _ProviderGenerator = class _ProviderGenerator {
|
|
899
1031
|
constructor(project, config) {
|
|
900
1032
|
__publicField(this, "project");
|
|
@@ -905,7 +1037,7 @@ var _ProviderGenerator = class _ProviderGenerator {
|
|
|
905
1037
|
this.clientName = config.clientName || "default";
|
|
906
1038
|
}
|
|
907
1039
|
generate(outputDir) {
|
|
908
|
-
const filePath =
|
|
1040
|
+
const filePath = path6.join(outputDir, "providers.ts");
|
|
909
1041
|
const sourceFile = this.project.createSourceFile(filePath, "", {
|
|
910
1042
|
overwrite: true
|
|
911
1043
|
});
|
|
@@ -1100,7 +1232,7 @@ var ProviderGenerator = _ProviderGenerator;
|
|
|
1100
1232
|
|
|
1101
1233
|
// src/lib/generators/utility/base-interceptor.generator.ts
|
|
1102
1234
|
var import_ts_morph4 = require("ts-morph");
|
|
1103
|
-
var
|
|
1235
|
+
var path7 = __toESM(require("path"));
|
|
1104
1236
|
var _project, _clientName;
|
|
1105
1237
|
var _BaseInterceptorGenerator = class _BaseInterceptorGenerator {
|
|
1106
1238
|
constructor(project, clientName = "default") {
|
|
@@ -1110,8 +1242,8 @@ var _BaseInterceptorGenerator = class _BaseInterceptorGenerator {
|
|
|
1110
1242
|
__privateSet(this, _clientName, clientName);
|
|
1111
1243
|
}
|
|
1112
1244
|
generate(outputDir) {
|
|
1113
|
-
const utilsDir =
|
|
1114
|
-
const filePath =
|
|
1245
|
+
const utilsDir = path7.join(outputDir, "utils");
|
|
1246
|
+
const filePath = path7.join(utilsDir, "base-interceptor.ts");
|
|
1115
1247
|
const sourceFile = __privateGet(this, _project).createSourceFile(filePath, "", {
|
|
1116
1248
|
overwrite: true
|
|
1117
1249
|
});
|
|
@@ -1234,7 +1366,7 @@ var BaseInterceptorGenerator = _BaseInterceptorGenerator;
|
|
|
1234
1366
|
|
|
1235
1367
|
// src/lib/generators/service/service.generator.ts
|
|
1236
1368
|
var import_ts_morph5 = require("ts-morph");
|
|
1237
|
-
var
|
|
1369
|
+
var path8 = __toESM(require("path"));
|
|
1238
1370
|
|
|
1239
1371
|
// src/lib/utils/string.utils.ts
|
|
1240
1372
|
function camelCase(str) {
|
|
@@ -2025,7 +2157,7 @@ var ServiceMethodGenerator = _ServiceMethodGenerator;
|
|
|
2025
2157
|
|
|
2026
2158
|
// src/lib/generators/service/service.generator.ts
|
|
2027
2159
|
var _ServiceGenerator = class _ServiceGenerator {
|
|
2028
|
-
constructor(
|
|
2160
|
+
constructor(parser, project, config) {
|
|
2029
2161
|
__publicField(this, "project");
|
|
2030
2162
|
__publicField(this, "parser");
|
|
2031
2163
|
__publicField(this, "spec");
|
|
@@ -2033,13 +2165,27 @@ var _ServiceGenerator = class _ServiceGenerator {
|
|
|
2033
2165
|
__publicField(this, "methodGenerator");
|
|
2034
2166
|
this.config = config;
|
|
2035
2167
|
this.project = project;
|
|
2036
|
-
this.parser =
|
|
2037
|
-
this.spec =
|
|
2168
|
+
this.parser = parser;
|
|
2169
|
+
this.spec = this.parser.getSpec();
|
|
2170
|
+
if (!this.parser.isValidSpec()) {
|
|
2171
|
+
const versionInfo = this.parser.getSpecVersion();
|
|
2172
|
+
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"}`);
|
|
2173
|
+
}
|
|
2038
2174
|
this.methodGenerator = new ServiceMethodGenerator(config);
|
|
2039
2175
|
}
|
|
2176
|
+
static create(swaggerPathOrUrl, project, config) {
|
|
2177
|
+
return __async(this, null, function* () {
|
|
2178
|
+
const parser = yield SwaggerParser.create(swaggerPathOrUrl);
|
|
2179
|
+
return new _ServiceGenerator(parser, project, config);
|
|
2180
|
+
});
|
|
2181
|
+
}
|
|
2040
2182
|
generate(outputRoot) {
|
|
2041
|
-
const outputDir =
|
|
2183
|
+
const outputDir = path8.join(outputRoot, "services");
|
|
2042
2184
|
const paths = this.extractPaths();
|
|
2185
|
+
if (paths.length === 0) {
|
|
2186
|
+
console.warn("No API paths found in the specification");
|
|
2187
|
+
return;
|
|
2188
|
+
}
|
|
2043
2189
|
const controllerGroups = this.groupPathsByController(paths);
|
|
2044
2190
|
Object.entries(controllerGroups).forEach(([controllerName, operations]) => {
|
|
2045
2191
|
this.generateServiceFile(controllerName, operations, outputDir);
|
|
@@ -2048,7 +2194,7 @@ var _ServiceGenerator = class _ServiceGenerator {
|
|
|
2048
2194
|
extractPaths() {
|
|
2049
2195
|
const paths = [];
|
|
2050
2196
|
const swaggerPaths = this.spec.paths || {};
|
|
2051
|
-
Object.entries(swaggerPaths).forEach(([
|
|
2197
|
+
Object.entries(swaggerPaths).forEach(([path10, pathItem]) => {
|
|
2052
2198
|
const methods = [
|
|
2053
2199
|
"get",
|
|
2054
2200
|
"post",
|
|
@@ -2062,7 +2208,7 @@ var _ServiceGenerator = class _ServiceGenerator {
|
|
|
2062
2208
|
if (pathItem[method]) {
|
|
2063
2209
|
const operation = pathItem[method];
|
|
2064
2210
|
paths.push({
|
|
2065
|
-
path:
|
|
2211
|
+
path: path10,
|
|
2066
2212
|
method: method.toUpperCase(),
|
|
2067
2213
|
operationId: operation.operationId,
|
|
2068
2214
|
summary: operation.summary,
|
|
@@ -2094,12 +2240,12 @@ var _ServiceGenerator = class _ServiceGenerator {
|
|
|
2094
2240
|
}
|
|
2095
2241
|
groupPathsByController(paths) {
|
|
2096
2242
|
const groups = {};
|
|
2097
|
-
paths.forEach((
|
|
2243
|
+
paths.forEach((path10) => {
|
|
2098
2244
|
let controllerName = "Default";
|
|
2099
|
-
if (
|
|
2100
|
-
controllerName =
|
|
2245
|
+
if (path10.tags && path10.tags.length > 0) {
|
|
2246
|
+
controllerName = path10.tags[0];
|
|
2101
2247
|
} else {
|
|
2102
|
-
const pathParts =
|
|
2248
|
+
const pathParts = path10.path.split("/").filter((p) => p && !p.startsWith("{"));
|
|
2103
2249
|
if (pathParts.length > 1) {
|
|
2104
2250
|
controllerName = pascalCase(pathParts[1]);
|
|
2105
2251
|
}
|
|
@@ -2108,13 +2254,13 @@ var _ServiceGenerator = class _ServiceGenerator {
|
|
|
2108
2254
|
if (!groups[controllerName]) {
|
|
2109
2255
|
groups[controllerName] = [];
|
|
2110
2256
|
}
|
|
2111
|
-
groups[controllerName].push(
|
|
2257
|
+
groups[controllerName].push(path10);
|
|
2112
2258
|
});
|
|
2113
2259
|
return groups;
|
|
2114
2260
|
}
|
|
2115
2261
|
generateServiceFile(controllerName, operations, outputDir) {
|
|
2116
2262
|
const fileName = `${camelCase(controllerName)}.service.ts`;
|
|
2117
|
-
const filePath =
|
|
2263
|
+
const filePath = path8.join(outputDir, fileName);
|
|
2118
2264
|
const sourceFile = this.project.createSourceFile(filePath, "", {
|
|
2119
2265
|
overwrite: true
|
|
2120
2266
|
});
|
|
@@ -2311,15 +2457,15 @@ var ServiceGenerator = _ServiceGenerator;
|
|
|
2311
2457
|
|
|
2312
2458
|
// src/lib/generators/service/service-index.generator.ts
|
|
2313
2459
|
var fs2 = __toESM(require("fs"));
|
|
2314
|
-
var
|
|
2460
|
+
var path9 = __toESM(require("path"));
|
|
2315
2461
|
var _ServiceIndexGenerator = class _ServiceIndexGenerator {
|
|
2316
2462
|
constructor(project) {
|
|
2317
2463
|
__publicField(this, "project");
|
|
2318
2464
|
this.project = project;
|
|
2319
2465
|
}
|
|
2320
2466
|
generateIndex(outputRoot) {
|
|
2321
|
-
const servicesDir =
|
|
2322
|
-
const indexPath =
|
|
2467
|
+
const servicesDir = path9.join(outputRoot, "services");
|
|
2468
|
+
const indexPath = path9.join(servicesDir, "index.ts");
|
|
2323
2469
|
const sourceFile = this.project.createSourceFile(indexPath, "", {
|
|
2324
2470
|
overwrite: true
|
|
2325
2471
|
});
|
|
@@ -2342,14 +2488,38 @@ var ServiceIndexGenerator = _ServiceIndexGenerator;
|
|
|
2342
2488
|
|
|
2343
2489
|
// src/lib/core/generator.ts
|
|
2344
2490
|
var fs3 = __toESM(require("fs"));
|
|
2491
|
+
function isUrl(input) {
|
|
2492
|
+
try {
|
|
2493
|
+
new URL(input);
|
|
2494
|
+
return true;
|
|
2495
|
+
} catch (e) {
|
|
2496
|
+
return false;
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
__name(isUrl, "isUrl");
|
|
2500
|
+
function validateInput(input) {
|
|
2501
|
+
if (isUrl(input)) {
|
|
2502
|
+
const url = new URL(input);
|
|
2503
|
+
if (![
|
|
2504
|
+
"http:",
|
|
2505
|
+
"https:"
|
|
2506
|
+
].includes(url.protocol)) {
|
|
2507
|
+
throw new Error(`Unsupported URL protocol: ${url.protocol}. Only HTTP and HTTPS are supported.`);
|
|
2508
|
+
}
|
|
2509
|
+
} else {
|
|
2510
|
+
if (!fs3.existsSync(input)) {
|
|
2511
|
+
throw new Error(`Input file not found: ${input}`);
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
__name(validateInput, "validateInput");
|
|
2345
2516
|
function generateFromConfig(config) {
|
|
2346
2517
|
return __async(this, null, function* () {
|
|
2347
2518
|
var _a;
|
|
2348
|
-
|
|
2349
|
-
throw new Error(`Input file not found: ${config.input}`);
|
|
2350
|
-
}
|
|
2519
|
+
validateInput(config.input);
|
|
2351
2520
|
const outputPath = config.output;
|
|
2352
2521
|
const generateServices = (_a = config.options.generateServices) != null ? _a : true;
|
|
2522
|
+
const inputType = isUrl(config.input) ? "URL" : "file";
|
|
2353
2523
|
if (!fs3.existsSync(outputPath)) {
|
|
2354
2524
|
fs3.mkdirSync(outputPath, {
|
|
2355
2525
|
recursive: true
|
|
@@ -2364,7 +2534,8 @@ function generateFromConfig(config) {
|
|
|
2364
2534
|
strict: true
|
|
2365
2535
|
}, config.compilerOptions)
|
|
2366
2536
|
});
|
|
2367
|
-
|
|
2537
|
+
console.log(`\u{1F4E1} Processing OpenAPI specification from ${inputType}: ${config.input}`);
|
|
2538
|
+
const typeGenerator = yield TypeGenerator.create(config.input, outputPath, config);
|
|
2368
2539
|
typeGenerator.generate();
|
|
2369
2540
|
console.log(`\u2705 TypeScript interfaces generated`);
|
|
2370
2541
|
if (generateServices) {
|
|
@@ -2376,7 +2547,7 @@ function generateFromConfig(config) {
|
|
|
2376
2547
|
}
|
|
2377
2548
|
const fileDownloadHelper = new FileDownloadGenerator(project);
|
|
2378
2549
|
fileDownloadHelper.generate(outputPath);
|
|
2379
|
-
const serviceGenerator =
|
|
2550
|
+
const serviceGenerator = yield ServiceGenerator.create(config.input, project, config);
|
|
2380
2551
|
serviceGenerator.generate(outputPath);
|
|
2381
2552
|
const indexGenerator = new ServiceIndexGenerator(project);
|
|
2382
2553
|
indexGenerator.generateIndex(outputPath);
|
|
@@ -2388,14 +2559,19 @@ function generateFromConfig(config) {
|
|
|
2388
2559
|
}
|
|
2389
2560
|
const mainIndexGenerator = new MainIndexGenerator(project, config);
|
|
2390
2561
|
mainIndexGenerator.generateMainIndex(outputPath);
|
|
2562
|
+
const sourceInfo = `from ${inputType}: ${config.input}`;
|
|
2391
2563
|
if (config.clientName) {
|
|
2392
|
-
console.log(`\u{1F389} ${config.clientName} Generation completed successfully
|
|
2564
|
+
console.log(`\u{1F389} ${config.clientName} Generation completed successfully ${sourceInfo} -> ${outputPath}`);
|
|
2393
2565
|
} else {
|
|
2394
|
-
console.log(
|
|
2566
|
+
console.log(`\u{1F389} Generation completed successfully ${sourceInfo} -> ${outputPath}`);
|
|
2395
2567
|
}
|
|
2396
2568
|
} catch (error) {
|
|
2397
2569
|
if (error instanceof Error) {
|
|
2398
2570
|
console.error("\u274C Error during generation:", error.message);
|
|
2571
|
+
if (error.message.includes("fetch") || error.message.includes("Failed to fetch")) {
|
|
2572
|
+
console.error("\u{1F4A1} Tip: Make sure the URL is accessible and returns a valid OpenAPI/Swagger specification");
|
|
2573
|
+
console.error("\u{1F4A1} Alternative: Download the specification file locally and use the file path instead");
|
|
2574
|
+
}
|
|
2399
2575
|
} else {
|
|
2400
2576
|
console.error("\u274C Unknown error during generation:", error);
|
|
2401
2577
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ng-openapi",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.40-pr-8-feature-url-support.0",
|
|
4
4
|
"description": "Generate Angular services and TypeScript types from OpenAPI/Swagger specifications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -61,7 +61,8 @@
|
|
|
61
61
|
"ts-morph": "^26.0.0",
|
|
62
62
|
"ts-node": "^10.9.2",
|
|
63
63
|
"typescript": "^5.8.3",
|
|
64
|
-
"@types/swagger-schema-official": "^2.0.25"
|
|
64
|
+
"@types/swagger-schema-official": "^2.0.25",
|
|
65
|
+
"js-yaml": "^4.1.0"
|
|
65
66
|
},
|
|
66
67
|
"peerDependencies": {
|
|
67
68
|
"@angular/core": ">=15",
|