swaggular 0.3.0 → 0.6.1
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/dist/cli/args.js +6 -0
- package/dist/cli/variables.js +19 -4
- package/dist/core/data/types-data.d.ts +0 -0
- package/dist/core/data/types-data.js +1 -0
- package/dist/core/state/interface-state.d.ts +3 -0
- package/dist/core/state/interface-state.js +7 -0
- package/dist/core/state/swagger-state.d.ts +1 -0
- package/dist/core/state/swagger-state.js +3 -0
- package/dist/index.js +5 -3
- package/dist/renderers/generate-comments.js +1 -1
- package/dist/renderers/generate-interface.d.ts +5 -6
- package/dist/renderers/generate-interface.js +131 -38
- package/dist/renderers/generate-service.d.ts +2 -1
- package/dist/renderers/generate-service.js +67 -45
- package/dist/templates/services/angular-template.d.ts +3 -7
- package/dist/templates/services/angular-template.js +10 -2
- package/dist/types/config.d.ts +22 -0
- package/dist/types/config.js +2 -0
- package/dist/types/parsed-args.d.ts +5 -2
- package/dist/types/template.d.ts +11 -0
- package/dist/types/template.js +2 -0
- package/dist/utils/build-types.js +5 -12
- package/dist/utils/config-loader.d.ts +2 -0
- package/dist/utils/config-loader.js +53 -0
- package/dist/utils/location-factory.d.ts +1 -0
- package/dist/utils/location-factory.js +137 -0
- package/dist/utils/path-utils.d.ts +2 -0
- package/dist/utils/path-utils.js +20 -0
- package/dist/utils/string-utils.d.ts +0 -5
- package/dist/utils/string-utils.js +0 -5
- package/dist/utils/template-renderer.d.ts +2 -0
- package/dist/utils/template-renderer.js +72 -0
- package/package.json +1 -1
package/dist/cli/args.js
CHANGED
package/dist/cli/variables.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.toVariables = toVariables;
|
|
4
|
+
const config_loader_1 = require("../utils/config-loader");
|
|
4
5
|
function toVariables(parsed) {
|
|
5
6
|
const variables = {};
|
|
6
7
|
if (parsed.args.mode) {
|
|
@@ -11,11 +12,25 @@ function toVariables(parsed) {
|
|
|
11
12
|
variables.segmentsToIgnore = parsed.args.segmentsToIgnore.split(',');
|
|
12
13
|
}
|
|
13
14
|
}
|
|
15
|
+
const configPath = parsed.args.config;
|
|
16
|
+
const fileConfig = (0, config_loader_1.loadConfig)(configPath);
|
|
17
|
+
const mergedConfig = { ...fileConfig, ...variables };
|
|
14
18
|
return {
|
|
15
|
-
input: parsed.args.input ||
|
|
16
|
-
|
|
19
|
+
input: parsed.args.input ||
|
|
20
|
+
parsed.args.i ||
|
|
21
|
+
parsed.positional[0] ||
|
|
22
|
+
mergedConfig.input ||
|
|
23
|
+
'swagger.json',
|
|
24
|
+
output: parsed.args.output ||
|
|
25
|
+
parsed.args.o ||
|
|
26
|
+
parsed.positional[1] ||
|
|
27
|
+
mergedConfig.output ||
|
|
28
|
+
'results',
|
|
17
29
|
noGenerate: parsed.args.noGenerate || false,
|
|
18
|
-
groupingMode: variables.groupingMode || 'path',
|
|
19
|
-
segmentsToIgnore: variables.segmentsToIgnore || ['api'],
|
|
30
|
+
groupingMode: variables.groupingMode || mergedConfig.groupingMode || 'path',
|
|
31
|
+
segmentsToIgnore: variables.segmentsToIgnore || mergedConfig.segmentsToIgnore || ['api'],
|
|
32
|
+
// Pass entire config if needed for templates
|
|
33
|
+
templates: mergedConfig.templates,
|
|
34
|
+
types: mergedConfig.types,
|
|
20
35
|
};
|
|
21
36
|
}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -7,12 +7,15 @@ export interface InterfaceStateI {
|
|
|
7
7
|
declare class InterfaceState implements InterfaceStateI {
|
|
8
8
|
generatedInterfaces: Record<string, InterfaceData>;
|
|
9
9
|
generatedEnums: Record<string, InterfaceData>;
|
|
10
|
+
typeMappings: Record<string, string>;
|
|
10
11
|
componentsSchemas: Record<string, IJsonSchema>;
|
|
11
12
|
constructor();
|
|
12
13
|
setComponentsSchemas(componentsSchemas: Record<string, IJsonSchema>): void;
|
|
13
14
|
getComponentsSchema(name: string): IJsonSchema | undefined;
|
|
14
15
|
addInterfaces(interfacesData: InterfaceData[]): void;
|
|
15
16
|
getInterface(name: string): InterfaceData | undefined;
|
|
17
|
+
addTypeMapping(original: string, mapped: string): void;
|
|
18
|
+
getTypeMapping(original: string): string | undefined;
|
|
16
19
|
}
|
|
17
20
|
export declare const interfaceState: InterfaceState;
|
|
18
21
|
export {};
|
|
@@ -4,6 +4,7 @@ exports.interfaceState = void 0;
|
|
|
4
4
|
class InterfaceState {
|
|
5
5
|
generatedInterfaces = {};
|
|
6
6
|
generatedEnums = {};
|
|
7
|
+
typeMappings = {};
|
|
7
8
|
componentsSchemas = {};
|
|
8
9
|
constructor() { }
|
|
9
10
|
setComponentsSchemas(componentsSchemas) {
|
|
@@ -20,5 +21,11 @@ class InterfaceState {
|
|
|
20
21
|
getInterface(name) {
|
|
21
22
|
return this.generatedInterfaces[name] || this.generatedEnums[name];
|
|
22
23
|
}
|
|
24
|
+
addTypeMapping(original, mapped) {
|
|
25
|
+
this.typeMappings[original] = mapped;
|
|
26
|
+
}
|
|
27
|
+
getTypeMapping(original) {
|
|
28
|
+
return this.typeMappings[original];
|
|
29
|
+
}
|
|
23
30
|
}
|
|
24
31
|
exports.interfaceState = new InterfaceState();
|
|
@@ -8,6 +8,7 @@ export declare class SwaggerState {
|
|
|
8
8
|
setPathsGroupedByScope(pathsGroupedByScope: GroupedPaths): void;
|
|
9
9
|
getPaths(): OpenAPIV3.PathsObject | undefined;
|
|
10
10
|
getSchemas(): Record<string, OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject> | undefined;
|
|
11
|
+
getParameters(): Record<string, OpenAPIV3.ReferenceObject | OpenAPIV3.ParameterObject> | undefined;
|
|
11
12
|
getPathsGroupedByScope(): GroupedPaths | undefined;
|
|
12
13
|
getGroupByPath(path: string): import("../../types/grouped-paths").GroupedPath | undefined;
|
|
13
14
|
}
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.main = main;
|
|
5
5
|
const swagger_parser_1 = require("./parsers/swagger-parser");
|
|
6
|
+
const location_factory_1 = require("./utils/location-factory");
|
|
6
7
|
const generate_interface_1 = require("./renderers/generate-interface");
|
|
7
8
|
const generate_service_1 = require("./renderers/generate-service");
|
|
8
9
|
const args_1 = require("./cli/args");
|
|
@@ -18,10 +19,11 @@ async function main() {
|
|
|
18
19
|
segmentsToIgnore: variables.segmentsToIgnore,
|
|
19
20
|
ignoreVariables: true,
|
|
20
21
|
});
|
|
21
|
-
(0,
|
|
22
|
+
const locations = (0, location_factory_1.computeLocations)();
|
|
23
|
+
(0, generate_interface_1.generateInterfaces)(variables);
|
|
22
24
|
(0, generate_service_1.generateServices)();
|
|
23
|
-
const interfaceFiles = (0, generate_interface_1.generateInterfacesFiles)();
|
|
24
|
-
const serviceFiles = (0, generate_service_1.generateServiceFiles)();
|
|
25
|
+
const interfaceFiles = (0, generate_interface_1.generateInterfacesFiles)(locations, variables);
|
|
26
|
+
const serviceFiles = (0, generate_service_1.generateServiceFiles)(locations, variables.templates);
|
|
25
27
|
if (variables.noGenerate) {
|
|
26
28
|
console.log('Files not generated');
|
|
27
29
|
return;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { OpenAPIV3 } from 'openapi-types';
|
|
2
2
|
import { FileContent } from '../types/file-content';
|
|
3
|
-
import { GroupedPath } from '../types/grouped-paths';
|
|
4
3
|
import { InterfaceData, InterfaceDataProperty } from '../types/interface-data';
|
|
4
|
+
import { SwaggularConfig } from '../types/config';
|
|
5
5
|
export declare function parametersToIProperties(parameters: (OpenAPIV3.ParameterObject | OpenAPIV3.ReferenceObject)[]): InterfaceDataProperty[];
|
|
6
6
|
export declare function propertiesToIProperties(properties: Record<string, OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject>): InterfaceDataProperty[];
|
|
7
|
-
export declare function generateInterfaces(): void;
|
|
8
|
-
export declare function generateWithParameters(): void;
|
|
9
|
-
export declare function
|
|
10
|
-
export declare function
|
|
11
|
-
export declare function generateInterfacesFiles(locations?: Record<string, string[]>): FileContent[];
|
|
7
|
+
export declare function generateInterfaces(templatesConfig?: SwaggularConfig): void;
|
|
8
|
+
export declare function generateWithParameters(templatesConfig?: SwaggularConfig): void;
|
|
9
|
+
export declare function generateComponentsSchemas(templatesConfig?: SwaggularConfig): void;
|
|
10
|
+
export declare function generateInterfacesFiles(locations?: Record<string, string[]>, templatesConfig?: SwaggularConfig): FileContent[];
|
|
12
11
|
export declare function generateContent(interfaceData: InterfaceData, deep?: number): string;
|
|
@@ -4,14 +4,11 @@ exports.parametersToIProperties = parametersToIProperties;
|
|
|
4
4
|
exports.propertiesToIProperties = propertiesToIProperties;
|
|
5
5
|
exports.generateInterfaces = generateInterfaces;
|
|
6
6
|
exports.generateWithParameters = generateWithParameters;
|
|
7
|
-
exports.computeParametersName = computeParametersName;
|
|
8
7
|
exports.generateComponentsSchemas = generateComponentsSchemas;
|
|
9
8
|
exports.generateInterfacesFiles = generateInterfacesFiles;
|
|
10
9
|
exports.generateContent = generateContent;
|
|
11
10
|
const interface_state_1 = require("../core/state/interface-state");
|
|
12
11
|
const swagger_state_1 = require("../core/state/swagger-state");
|
|
13
|
-
const extends_from_types_1 = require("../templates/types/extends-from-types");
|
|
14
|
-
const generic_types_1 = require("../templates/types/generic-types");
|
|
15
12
|
const build_types_1 = require("../utils/build-types");
|
|
16
13
|
const object_utils_1 = require("../utils/object-utils");
|
|
17
14
|
const path_utils_1 = require("../utils/path-utils");
|
|
@@ -23,12 +20,22 @@ function parametersToIProperties(parameters) {
|
|
|
23
20
|
for (const param of parameters) {
|
|
24
21
|
if ((0, type_guard_1.isReference)(param)) {
|
|
25
22
|
const ref = param.$ref.split('/').pop();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
if (param.$ref.includes('/components/parameters/')) {
|
|
24
|
+
const parameters = swagger_state_1.swaggerState.getParameters();
|
|
25
|
+
const parameter = parameters?.[ref];
|
|
26
|
+
if (parameter && !(0, type_guard_1.isReference)(parameter)) {
|
|
27
|
+
if (parameter.in !== 'query')
|
|
28
|
+
continue;
|
|
29
|
+
const tsType = parameter.schema ? (0, build_types_1.switchTypeJson)(parameter.schema) : 'any';
|
|
30
|
+
properties.push({
|
|
31
|
+
name: (0, string_utils_1.lowerFirst)(parameter.name),
|
|
32
|
+
type: tsType,
|
|
33
|
+
optional: !parameter.required,
|
|
34
|
+
comments: (0, generate_comments_1.generateInterfaceComments)(parameter.schema),
|
|
35
|
+
});
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
32
39
|
continue;
|
|
33
40
|
}
|
|
34
41
|
if (param.in !== 'query')
|
|
@@ -65,16 +72,52 @@ function propertiesToIProperties(properties) {
|
|
|
65
72
|
}
|
|
66
73
|
return interfaceDataProperties;
|
|
67
74
|
}
|
|
68
|
-
function
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
function isGenericType(data, genericTypes) {
|
|
76
|
+
for (const genericType of genericTypes) {
|
|
77
|
+
if (genericType.properties.length !== data.properties.length)
|
|
78
|
+
continue;
|
|
79
|
+
const dataProperties = data.properties.map((p) => p.name);
|
|
80
|
+
const genericTypeProperties = genericType.properties.map((p) => p.name);
|
|
81
|
+
if (genericTypeProperties.some((p) => !dataProperties.includes(p)))
|
|
82
|
+
continue;
|
|
83
|
+
return true; // Match found
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
function computeExtendsFromType(data, extendsTypes) {
|
|
88
|
+
for (const extendsFromType of extendsTypes) {
|
|
89
|
+
const dataPropertiesReq = data.properties.filter((p) => !p.optional).map((p) => p.name);
|
|
90
|
+
const extendsFromTypePropertiesReq = extendsFromType.properties
|
|
91
|
+
.filter((p) => !p.optional)
|
|
92
|
+
.map((p) => p.name);
|
|
93
|
+
if (extendsFromTypePropertiesReq.some((p) => !dataPropertiesReq.includes(p))) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (!extendsFromType.properties.some((ep) => data.properties.some((dp) => dp.name === ep.name))) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const extensionProperties = extendsFromType.properties.map((p) => p.name.toLowerCase());
|
|
100
|
+
return {
|
|
101
|
+
...data,
|
|
102
|
+
properties: data.properties.filter((p) => !extensionProperties.includes(p.name.toLowerCase())),
|
|
103
|
+
imports: [...data.imports, extendsFromType.name],
|
|
104
|
+
extendsFrom: [...(data.extendsFrom ?? []), extendsFromType.name],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return data;
|
|
71
108
|
}
|
|
72
|
-
function
|
|
109
|
+
function generateInterfaces(templatesConfig) {
|
|
110
|
+
generateComponentsSchemas(templatesConfig);
|
|
111
|
+
generateWithParameters(templatesConfig);
|
|
112
|
+
}
|
|
113
|
+
function generateWithParameters(templatesConfig) {
|
|
73
114
|
const paths = swagger_state_1.swaggerState.getPaths();
|
|
74
115
|
const groupedPaths = swagger_state_1.swaggerState.getPathsGroupedByScope();
|
|
75
116
|
if (!paths || !groupedPaths)
|
|
76
117
|
return;
|
|
77
118
|
const interfacesData = [];
|
|
119
|
+
const genericTypes = templatesConfig?.types?.generic ?? [];
|
|
120
|
+
const extendsTypes = templatesConfig?.types?.extendsFrom ?? [];
|
|
78
121
|
for (const groupedPath of Object.values(groupedPaths)) {
|
|
79
122
|
for (const innerPath of groupedPath.paths) {
|
|
80
123
|
const pathInfo = paths[innerPath];
|
|
@@ -94,40 +137,31 @@ function generateWithParameters() {
|
|
|
94
137
|
const properties = parametersToIProperties(parameters);
|
|
95
138
|
if (properties.length === 0)
|
|
96
139
|
continue;
|
|
97
|
-
const interfaceName = computeParametersName(httpMethod, innerPath, groupedPath);
|
|
140
|
+
const interfaceName = (0, path_utils_1.computeParametersName)(httpMethod, innerPath, groupedPath);
|
|
98
141
|
const interData = {
|
|
99
142
|
name: interfaceName,
|
|
100
143
|
type: 'interface',
|
|
101
144
|
imports: properties.filter((p) => !(0, type_guard_1.isNativeType)(p.type)).map((p) => p.type),
|
|
102
145
|
properties,
|
|
103
146
|
};
|
|
104
|
-
const generic = (
|
|
147
|
+
const generic = isGenericType(interData, genericTypes);
|
|
105
148
|
if (generic)
|
|
106
149
|
continue;
|
|
107
|
-
const interDataWithExtendsFrom = (
|
|
150
|
+
const interDataWithExtendsFrom = computeExtendsFromType(interData, extendsTypes);
|
|
108
151
|
interfacesData.push(interDataWithExtendsFrom);
|
|
109
152
|
}
|
|
110
153
|
}
|
|
111
154
|
}
|
|
112
155
|
interface_state_1.interfaceState.addInterfaces(interfacesData);
|
|
113
156
|
}
|
|
114
|
-
function
|
|
115
|
-
const dict = {
|
|
116
|
-
get: '',
|
|
117
|
-
post: 'Create',
|
|
118
|
-
put: 'Update',
|
|
119
|
-
delete: 'Delete',
|
|
120
|
-
patch: 'Patch',
|
|
121
|
-
};
|
|
122
|
-
const name = dict[method];
|
|
123
|
-
const extra = (0, string_utils_1.kebabToPascalCase)((0, path_utils_1.removeVariablesFromPath)((0, path_utils_1.getExtraSegments)(innerPath, (0, path_utils_1.createBaseUrl)(groupedPath.baseSegments)).join('')));
|
|
124
|
-
return name + groupedPath.groupName + (0, string_utils_1.upFirst)(extra) + 'Params';
|
|
125
|
-
}
|
|
126
|
-
function generateComponentsSchemas() {
|
|
157
|
+
function generateComponentsSchemas(templatesConfig) {
|
|
127
158
|
const schemas = swagger_state_1.swaggerState.getSchemas();
|
|
128
159
|
if (!schemas)
|
|
129
160
|
return;
|
|
130
161
|
const interfaces = [];
|
|
162
|
+
const genericMappings = new Map();
|
|
163
|
+
const genericTypes = templatesConfig?.types?.generic ?? [];
|
|
164
|
+
const extendsTypes = templatesConfig?.types?.extendsFrom ?? [];
|
|
131
165
|
for (const [key, value] of Object.entries(schemas)) {
|
|
132
166
|
if ((0, type_guard_1.isReference)(value))
|
|
133
167
|
continue;
|
|
@@ -147,6 +181,29 @@ function generateComponentsSchemas() {
|
|
|
147
181
|
}),
|
|
148
182
|
};
|
|
149
183
|
interfaces.push(interData);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const targetType = (0, build_types_1.switchTypeJson)(value);
|
|
187
|
+
if (targetType) {
|
|
188
|
+
const imports = [];
|
|
189
|
+
const baseType = targetType.replace('[]', '');
|
|
190
|
+
if (!(0, type_guard_1.isNativeType)(baseType)) {
|
|
191
|
+
imports.push(baseType);
|
|
192
|
+
}
|
|
193
|
+
interfaces.push({
|
|
194
|
+
name: key,
|
|
195
|
+
type: 'type',
|
|
196
|
+
imports,
|
|
197
|
+
properties: [
|
|
198
|
+
{
|
|
199
|
+
name: '',
|
|
200
|
+
type: targetType,
|
|
201
|
+
optional: false,
|
|
202
|
+
comments: (0, generate_comments_1.generateInterfaceComments)(value),
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
});
|
|
206
|
+
continue;
|
|
150
207
|
}
|
|
151
208
|
continue;
|
|
152
209
|
}
|
|
@@ -161,24 +218,56 @@ function generateComponentsSchemas() {
|
|
|
161
218
|
}),
|
|
162
219
|
properties,
|
|
163
220
|
};
|
|
164
|
-
const generic = (
|
|
221
|
+
const generic = isGenericType(interData, genericTypes);
|
|
165
222
|
if (generic) {
|
|
223
|
+
const genericType = genericTypes.find((g) => {
|
|
224
|
+
if (g.properties.length !== interData.properties.length)
|
|
225
|
+
return false;
|
|
226
|
+
const gProps = g.properties.map((p) => p.name);
|
|
227
|
+
const dProps = interData.properties.map((p) => p.name);
|
|
228
|
+
return !gProps.some((p) => !dProps.includes(p));
|
|
229
|
+
});
|
|
230
|
+
if (genericType) {
|
|
231
|
+
const genericName = computeGenericType(interData, genericType);
|
|
232
|
+
genericMappings.set(key, genericName);
|
|
233
|
+
interface_state_1.interfaceState.addTypeMapping(key, genericName);
|
|
234
|
+
}
|
|
166
235
|
continue;
|
|
167
236
|
}
|
|
168
|
-
const interDataWithExtendsFrom = (
|
|
237
|
+
const interDataWithExtendsFrom = computeExtendsFromType(interData, extendsTypes);
|
|
169
238
|
interfaces.push(interDataWithExtendsFrom);
|
|
170
239
|
}
|
|
240
|
+
// Update properties of all interfaces to use generic mappings
|
|
241
|
+
for (const inter of interfaces) {
|
|
242
|
+
inter.properties.forEach((p) => {
|
|
243
|
+
if (genericMappings.has(p.type)) {
|
|
244
|
+
p.type = genericMappings.get(p.type);
|
|
245
|
+
}
|
|
246
|
+
else if (p.type.endsWith('[]')) {
|
|
247
|
+
const base = p.type.replace('[]', '');
|
|
248
|
+
if (genericMappings.has(base)) {
|
|
249
|
+
p.type = genericMappings.get(base) + '[]';
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
171
254
|
interface_state_1.interfaceState.addInterfaces(interfaces);
|
|
172
255
|
}
|
|
173
|
-
function
|
|
256
|
+
function computeGenericType(newInterface, genericType) {
|
|
257
|
+
const typeParam = newInterface.name.replace(genericType.name, '');
|
|
258
|
+
return `${genericType.name}<${typeParam}>`;
|
|
259
|
+
}
|
|
260
|
+
function generateInterfacesFiles(locations, templatesConfig) {
|
|
261
|
+
const genericTypes = templatesConfig?.types?.generic ?? [];
|
|
262
|
+
const extendsTypes = templatesConfig?.types?.extendsFrom ?? [];
|
|
174
263
|
const interfacesData = [
|
|
175
|
-
...
|
|
176
|
-
...
|
|
264
|
+
...genericTypes,
|
|
265
|
+
...extendsTypes,
|
|
177
266
|
...Array.from(Object.values(interface_state_1.interfaceState.generatedInterfaces)),
|
|
178
267
|
];
|
|
179
268
|
const filesContent = [];
|
|
180
|
-
for (const
|
|
181
|
-
const location = [value.type === 'enum' ? 'enums' : 'dtos', ...(locations?.[
|
|
269
|
+
for (const value of interfacesData) {
|
|
270
|
+
const location = [value.type === 'enum' ? 'enums' : 'dtos', ...(locations?.[value.name] ?? [])];
|
|
182
271
|
const content = generateContent(value, location.length + 1);
|
|
183
272
|
const extraName = value.type === 'enum' ? 'enum' : 'dto';
|
|
184
273
|
filesContent.push({
|
|
@@ -200,16 +289,20 @@ function generateContent(interfaceData, deep = 0) {
|
|
|
200
289
|
.map((p) => p.type.replace('[]', ''));
|
|
201
290
|
const content = `${importsTemplate}\n\nexport interface ${interfaceData.name}${genericTypes.length > 0 ? `<${genericTypes.join(', ')}>` : ''} ${interfaceData.extendsFrom && interfaceData.extendsFrom.length > 0 ? `extends ${interfaceData.extendsFrom.join(', ')}` : ''} {
|
|
202
291
|
${interfaceData.properties
|
|
203
|
-
.map((p) => `${p.comments}\n
|
|
292
|
+
.map((p) => `${p.comments}\n ${(0, string_utils_1.lowerFirst)(p.name)}${p.optional ? '?' : ''}: ${p.type};`)
|
|
204
293
|
.join('\n')}
|
|
205
294
|
}`;
|
|
206
295
|
return content;
|
|
207
296
|
}
|
|
208
297
|
if (interfaceData.type === 'enum') {
|
|
209
298
|
const content = `export enum ${interfaceData.name} {
|
|
210
|
-
${interfaceData.properties.map((p) => `${p.comments}\n
|
|
299
|
+
${interfaceData.properties.map((p) => `${p.comments}\n ${p.name} = '${p.type}',`).join('\n')}
|
|
211
300
|
}`;
|
|
212
301
|
return content;
|
|
213
302
|
}
|
|
303
|
+
if (interfaceData.type === 'type') {
|
|
304
|
+
const targetType = interfaceData.properties[0].type;
|
|
305
|
+
return `${importsTemplate}\n\nexport type ${interfaceData.name} = ${targetType};`;
|
|
306
|
+
}
|
|
214
307
|
return '';
|
|
215
308
|
}
|
|
@@ -2,9 +2,10 @@ import { OpenAPIV3 } from 'openapi-types';
|
|
|
2
2
|
import { FileContent } from '../types/file-content';
|
|
3
3
|
import { GroupedPath } from '../types/grouped-paths';
|
|
4
4
|
import { ServiceDataMethod } from '../types/service-data';
|
|
5
|
+
import { SwaggularConfig } from '../types/config';
|
|
5
6
|
/**
|
|
6
7
|
* Pick the paths grouped by scope and generate services for them.
|
|
7
8
|
*/
|
|
8
9
|
export declare function generateServices(): void;
|
|
9
|
-
export declare function generateServiceFiles(locations?: Record<string, string[]
|
|
10
|
+
export declare function generateServiceFiles(locations?: Record<string, string[]>, templatesConfig?: SwaggularConfig['templates']): FileContent[];
|
|
10
11
|
export declare function buildMethods(groupedPath: GroupedPath, pathData: OpenAPIV3.PathsObject, serviceName: string): ServiceDataMethod[];
|
|
@@ -5,16 +5,15 @@ exports.generateServiceFiles = generateServiceFiles;
|
|
|
5
5
|
exports.buildMethods = buildMethods;
|
|
6
6
|
const service_state_1 = require("../core/state/service-state");
|
|
7
7
|
const swagger_state_1 = require("../core/state/swagger-state");
|
|
8
|
-
const angular_template_1 = require("../templates/services/angular-template");
|
|
9
8
|
const build_types_1 = require("../utils/build-types");
|
|
10
9
|
const path_utils_1 = require("../utils/path-utils");
|
|
11
10
|
const string_utils_1 = require("../utils/string-utils");
|
|
12
11
|
const type_guard_1 = require("../utils/type-guard");
|
|
13
12
|
const generate_comments_1 = require("./generate-comments");
|
|
14
13
|
const object_utils_1 = require("../utils/object-utils");
|
|
15
|
-
const
|
|
16
|
-
const http_params_handler_1 = require("../templates/services/http-params-handler");
|
|
14
|
+
const path_utils_2 = require("../utils/path-utils");
|
|
17
15
|
const interface_state_1 = require("../core/state/interface-state");
|
|
16
|
+
const template_renderer_1 = require("../utils/template-renderer");
|
|
18
17
|
/**
|
|
19
18
|
* Pick the paths grouped by scope and generate services for them.
|
|
20
19
|
*/
|
|
@@ -64,24 +63,36 @@ function addTypeToImports(type, imports) {
|
|
|
64
63
|
imports.add(baseType);
|
|
65
64
|
}
|
|
66
65
|
}
|
|
67
|
-
function generateServiceFiles(locations) {
|
|
66
|
+
function generateServiceFiles(locations, templatesConfig) {
|
|
68
67
|
const services = Object.values(service_state_1.serviceState.services);
|
|
69
68
|
const filesContent = [];
|
|
70
69
|
for (const service of services) {
|
|
71
70
|
const location = ['services', ...(locations?.[service.name] ?? [])];
|
|
71
|
+
const serviceTemplateConfig = templatesConfig?.service;
|
|
72
|
+
const templatePath = serviceTemplateConfig?.path || 'templates/angular-service.template';
|
|
72
73
|
const methods = service.methods
|
|
73
74
|
.map((method) => {
|
|
74
|
-
return buildMethodTemplate(method);
|
|
75
|
+
return buildMethodTemplate(method, serviceTemplateConfig?.options?.httpParamsHandler);
|
|
75
76
|
})
|
|
76
77
|
.join('\n\n');
|
|
78
|
+
const allImports = Array.from(new Set([...service.imports])).sort();
|
|
79
|
+
const hasHttpParams = service.methods.some((m) => !!m.queryParamType);
|
|
77
80
|
const templateParams = {
|
|
78
81
|
name: service.name,
|
|
79
82
|
baseUrl: service.baseUrl,
|
|
80
83
|
methods: methods,
|
|
81
|
-
imports:
|
|
82
|
-
|
|
84
|
+
imports: allImports.join(', '),
|
|
85
|
+
modelImports: allImports.length > 0
|
|
86
|
+
? `import { ${allImports.join(', ')} } from '${'../'.repeat(location.length)}models';`
|
|
87
|
+
: '',
|
|
88
|
+
hasHttpParamsHandler: hasHttpParams,
|
|
89
|
+
httpParamsHandler: serviceTemplateConfig?.options?.httpParamsHandler,
|
|
90
|
+
httpParamsHandlerImport: hasHttpParams
|
|
91
|
+
? serviceTemplateConfig?.options?.httpParamsHandlerImport
|
|
92
|
+
: '',
|
|
93
|
+
extraAngularImports: hasHttpParams && !serviceTemplateConfig?.options?.httpParamsHandler ? ', HttpParams' : '',
|
|
83
94
|
};
|
|
84
|
-
const content = (0,
|
|
95
|
+
const content = (0, template_renderer_1.renderServiceTemplate)(templatePath, templateParams);
|
|
85
96
|
filesContent.push({
|
|
86
97
|
location: location,
|
|
87
98
|
content,
|
|
@@ -107,7 +118,7 @@ function buildMethods(groupedPath, pathData, serviceName) {
|
|
|
107
118
|
const queryParams = parameters.filter((p) => p.in === 'query');
|
|
108
119
|
let queryParamType;
|
|
109
120
|
if (queryParams.length > 0) {
|
|
110
|
-
queryParamType = (0,
|
|
121
|
+
queryParamType = (0, path_utils_2.computeParametersName)(method, path, groupedPath);
|
|
111
122
|
}
|
|
112
123
|
const commentParams = parameters.filter((p) => p.in !== 'query');
|
|
113
124
|
if (queryParamType) {
|
|
@@ -140,18 +151,7 @@ function pathItemToMethods(pathItem) {
|
|
|
140
151
|
patch: pathItem.patch,
|
|
141
152
|
});
|
|
142
153
|
}
|
|
143
|
-
/**
|
|
144
|
-
* buildMethodName: High-performance semantic naming algorithm.
|
|
145
|
-
*
|
|
146
|
-
* This engine generates clean, non-redundant, and intuitive method names by
|
|
147
|
-
* analyzing the relationship between the base URL and extra path segments.
|
|
148
|
-
*
|
|
149
|
-
* For Mutation methods (POST, PUT, PATCH, DELETE), it prioritizes using the
|
|
150
|
-
* path segments as the method name itself if they describe an action,
|
|
151
|
-
* avoiding generic "create" or "update" prefixes where possible.
|
|
152
|
-
*/
|
|
153
154
|
function buildMethodName(method, path, groupedPath, usedNames, serviceName) {
|
|
154
|
-
// 1. Core mapping of HTTP verbs to professional action prefixes
|
|
155
155
|
const verbMap = {
|
|
156
156
|
get: 'get',
|
|
157
157
|
post: 'create',
|
|
@@ -161,7 +161,6 @@ function buildMethodName(method, path, groupedPath, usedNames, serviceName) {
|
|
|
161
161
|
};
|
|
162
162
|
const defaultVerb = verbMap[method.toLowerCase()] || method.toLowerCase();
|
|
163
163
|
const segments = (0, path_utils_1.getExtraSegments)(path, groupedPath.baseUrl);
|
|
164
|
-
// 2. Redundancy Filtering: Remove segments already implied by the service name
|
|
165
164
|
const serviceWords = serviceName.split(/(?=[A-Z])/).map((w) => w.toLowerCase());
|
|
166
165
|
const cleanSegments = segments.filter((seg) => {
|
|
167
166
|
if ((0, path_utils_1.isVariable)(seg))
|
|
@@ -169,7 +168,6 @@ function buildMethodName(method, path, groupedPath, usedNames, serviceName) {
|
|
|
169
168
|
const segWords = seg.split('-').map((w) => w.toLowerCase());
|
|
170
169
|
return !segWords.every((sw) => serviceWords.includes(sw));
|
|
171
170
|
});
|
|
172
|
-
// 3. Structural Analysis: Identify resources and variable positioning
|
|
173
171
|
const staticBefore = [];
|
|
174
172
|
const variableParts = [];
|
|
175
173
|
const staticAfter = [];
|
|
@@ -187,7 +185,6 @@ function buildMethodName(method, path, groupedPath, usedNames, serviceName) {
|
|
|
187
185
|
}
|
|
188
186
|
}
|
|
189
187
|
const nameParts = [];
|
|
190
|
-
// 4. Logic specific to data fetching (GET)
|
|
191
188
|
if (method.toLowerCase() === 'get') {
|
|
192
189
|
nameParts.push('get');
|
|
193
190
|
if (staticAfter.length > 0) {
|
|
@@ -203,22 +200,18 @@ function buildMethodName(method, path, groupedPath, usedNames, serviceName) {
|
|
|
203
200
|
nameParts.push('ById');
|
|
204
201
|
}
|
|
205
202
|
}
|
|
206
|
-
// 5. Logic for state mutation (POST, PUT, PATCH, DELETE)
|
|
207
203
|
else {
|
|
208
204
|
const actionParts = [...staticBefore, ...staticAfter];
|
|
209
205
|
if (actionParts.length > 0) {
|
|
210
|
-
// If the path contains an action (e.g., /assign, /send-to-user), use it directly.
|
|
211
206
|
nameParts.push((0, string_utils_1.toPascalCase)(actionParts));
|
|
212
207
|
}
|
|
213
208
|
else {
|
|
214
|
-
// No extra segments, use the default verb prefix (e.g., create, update)
|
|
215
209
|
nameParts.push((0, string_utils_1.upFirst)(defaultVerb));
|
|
216
210
|
}
|
|
217
211
|
if (variableParts.length > 0) {
|
|
218
212
|
nameParts.push('ById');
|
|
219
213
|
}
|
|
220
214
|
}
|
|
221
|
-
// 6. Pattern-based Refinement: Detect and prioritize standard REST suffixes
|
|
222
215
|
const normalizedPath = path.toLowerCase();
|
|
223
216
|
const suffixes = [
|
|
224
217
|
{ pattern: '/all', label: 'All' },
|
|
@@ -236,17 +229,13 @@ function buildMethodName(method, path, groupedPath, usedNames, serviceName) {
|
|
|
236
229
|
break;
|
|
237
230
|
}
|
|
238
231
|
}
|
|
239
|
-
// 7. Base candidate assembly
|
|
240
232
|
let candidateName = (0, string_utils_1.lowerFirst)(nameParts.join(''));
|
|
241
|
-
// Simplification for the primary resource GET
|
|
242
233
|
if (candidateName === 'getById' && !usedNames.includes('get')) {
|
|
243
234
|
candidateName = 'get';
|
|
244
235
|
}
|
|
245
|
-
// Fallback for empty/ambiguous names
|
|
246
236
|
if (candidateName === '' || (candidateName === defaultVerb && segments.length > 0)) {
|
|
247
237
|
candidateName = (0, string_utils_1.lowerFirst)(defaultVerb + (0, string_utils_1.toPascalCase)(segments.map((s) => s.replace(/[{}]/g, ''))));
|
|
248
238
|
}
|
|
249
|
-
// 8. Multi-strategy Collision Resolution
|
|
250
239
|
let finalName = candidateName;
|
|
251
240
|
let counter = 0;
|
|
252
241
|
while (usedNames.includes(finalName)) {
|
|
@@ -271,14 +260,26 @@ function buildParameters(operation, pathParameters) {
|
|
|
271
260
|
for (const param of allParams) {
|
|
272
261
|
if ((0, type_guard_1.isReference)(param)) {
|
|
273
262
|
const ref = param.$ref.split('/').pop();
|
|
274
|
-
console.log('🚀 ~ buildParameters ~ ref:', ref);
|
|
275
263
|
const interfaceData = interface_state_1.interfaceState.getInterface(ref);
|
|
276
|
-
|
|
264
|
+
let paramType = interface_state_1.interfaceState.getTypeMapping(ref) || ref;
|
|
265
|
+
let inType = interfaceData?.type !== undefined ? 'query' : 'path';
|
|
266
|
+
let required = true;
|
|
267
|
+
if (param.$ref.includes('/components/parameters/')) {
|
|
268
|
+
const parameters = swagger_state_1.swaggerState.getParameters();
|
|
269
|
+
const parameter = parameters?.[ref];
|
|
270
|
+
if (parameter && !(0, type_guard_1.isReference)(parameter)) {
|
|
271
|
+
inType = parameter.in;
|
|
272
|
+
required = parameter.required ?? false;
|
|
273
|
+
if (parameter.schema) {
|
|
274
|
+
paramType = (0, build_types_1.switchTypeJson)(parameter.schema);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
277
278
|
results.push({
|
|
278
279
|
name: (0, string_utils_1.lowerFirst)(ref),
|
|
279
|
-
in:
|
|
280
|
-
required
|
|
281
|
-
type:
|
|
280
|
+
in: inType,
|
|
281
|
+
required,
|
|
282
|
+
type: paramType,
|
|
282
283
|
});
|
|
283
284
|
continue;
|
|
284
285
|
}
|
|
@@ -308,21 +309,22 @@ function buildParameters(operation, pathParameters) {
|
|
|
308
309
|
type: bodyType,
|
|
309
310
|
});
|
|
310
311
|
}
|
|
311
|
-
console.log('🚀 ~ buildParameters ~ results:', results);
|
|
312
312
|
return results;
|
|
313
313
|
}
|
|
314
314
|
function buildResponseType(responses) {
|
|
315
315
|
const success = responses['200'] || responses['201'] || responses['204'] || responses['default'];
|
|
316
316
|
if (!success)
|
|
317
317
|
return 'any';
|
|
318
|
-
if ((0, type_guard_1.isReference)(success))
|
|
319
|
-
|
|
318
|
+
if ((0, type_guard_1.isReference)(success)) {
|
|
319
|
+
const name = success.$ref.split('/').pop();
|
|
320
|
+
return interface_state_1.interfaceState.getTypeMapping(name) || name;
|
|
321
|
+
}
|
|
320
322
|
const content = success.content?.['application/json'];
|
|
321
323
|
if (!content || !content.schema)
|
|
322
324
|
return 'any';
|
|
323
325
|
return (0, build_types_1.switchTypeJson)(content.schema);
|
|
324
326
|
}
|
|
325
|
-
function buildMethodTemplate(method) {
|
|
327
|
+
function buildMethodTemplate(method, httpParamsHandlerTemplate) {
|
|
326
328
|
const pathParams = method.parameters.filter((p) => p.in === 'path');
|
|
327
329
|
const bodyParam = method.parameters.find((p) => p.in === 'body');
|
|
328
330
|
const methodArgs = [];
|
|
@@ -339,10 +341,8 @@ function buildMethodTemplate(method) {
|
|
|
339
341
|
.map((s) => ((0, path_utils_1.isVariable)(s) ? `\${${(0, string_utils_1.lowerFirst)(s.replace(/[{}]/g, ''))}}` : s))
|
|
340
342
|
.join('/');
|
|
341
343
|
const url = `\`\${this.baseUrl}${pathStr ? '/' + pathStr : ''}\``;
|
|
342
|
-
let handler = '';
|
|
343
344
|
const optionsList = [];
|
|
344
345
|
if (method.queryParamType) {
|
|
345
|
-
handler = `\n\t\t${(0, http_params_handler_1.httpParamsHandler)('queryParams')}`;
|
|
346
346
|
optionsList.push('params');
|
|
347
347
|
}
|
|
348
348
|
if (['Blob', 'File'].includes(method.responseType)) {
|
|
@@ -358,8 +358,30 @@ function buildMethodTemplate(method) {
|
|
|
358
358
|
: ['post', 'put', 'patch'].includes(method.method)
|
|
359
359
|
? `this.http.${method.method}${genericType}(${url}, {}${options})`
|
|
360
360
|
: `this.http.${method.method}${genericType}(${url}${options})`;
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
361
|
+
let paramsLogic = '';
|
|
362
|
+
if (method.queryParamType) {
|
|
363
|
+
if (httpParamsHandlerTemplate) {
|
|
364
|
+
paramsLogic = `\n ${httpParamsHandlerTemplate.replace('${params}', 'queryParams')}`;
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
paramsLogic = `
|
|
368
|
+
let params = new HttpParams();
|
|
369
|
+
if (queryParams) {
|
|
370
|
+
for (const [key, value] of Object.entries(queryParams)) {
|
|
371
|
+
if (value === undefined || value === null) continue;
|
|
372
|
+
if (Array.isArray(value)) {
|
|
373
|
+
value.forEach((v) => (params = params.append(key, v)));
|
|
374
|
+
} else if (typeof value === 'object' && !(value instanceof Date) && !(value instanceof Blob)) {
|
|
375
|
+
params = params.set(key, JSON.stringify(value));
|
|
376
|
+
} else {
|
|
377
|
+
params = params.set(key, value as any);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}`;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return `${method.comments}
|
|
384
|
+
${method.name}(${argsString}): Observable<${method.responseType}> {${paramsLogic}
|
|
385
|
+
return ${methodCall};
|
|
364
386
|
}`;
|
|
365
387
|
}
|
|
@@ -7,12 +7,8 @@ export interface AngularServiceTemplate {
|
|
|
7
7
|
methods: string;
|
|
8
8
|
imports: string;
|
|
9
9
|
hasHttpParamsHandler: boolean;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
parametersTypes: Record<string, {
|
|
14
|
-
method: string;
|
|
15
|
-
parameterType: string;
|
|
16
|
-
}>;
|
|
10
|
+
configImports?: string[];
|
|
11
|
+
httpParamsHandler?: string;
|
|
12
|
+
httpParamsHandlerImport?: string;
|
|
17
13
|
}
|
|
18
14
|
export declare const angularTemplate: (params: AngularServiceTemplate) => string;
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.angularTemplate = void 0;
|
|
4
|
-
const http_params_handler_1 = require("./http-params-handler");
|
|
5
4
|
const angularTemplate = (params) => {
|
|
5
|
+
const configImports = params.configImports
|
|
6
|
+
? params.configImports.map((i) => `import { ${i} } from '@angular/core';`).join('\n') // Placeholder logic, actual imports might be more complex
|
|
7
|
+
: '';
|
|
8
|
+
// Actually, let's keep it simple. The config imports should probably be raw strings or structured.
|
|
9
|
+
// For now, let's assume the template receives the RAW import strings from the config if provided,
|
|
10
|
+
// OR we construct them here.
|
|
11
|
+
// User wants: "can make it so it can work without any custom template (just the most generic one)"
|
|
12
|
+
// and "make it so it works with a JSON configuration".
|
|
13
|
+
const httpParamsHandlerImport = params.httpParamsHandlerImport || '';
|
|
6
14
|
return `
|
|
7
15
|
import { HttpClient } from "@angular/common/http";
|
|
8
16
|
import { Observable } from "rxjs";
|
|
9
17
|
import { inject, Injectable } from '@angular/core';
|
|
10
18
|
import { ${params.imports} } from '../models';
|
|
11
|
-
${
|
|
19
|
+
${httpParamsHandlerImport}
|
|
12
20
|
|
|
13
21
|
@Injectable({
|
|
14
22
|
providedIn: "root"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ServiceTemplateParams } from './template';
|
|
2
|
+
import { InterfaceData } from './interface-data';
|
|
3
|
+
export interface SwaggularConfig {
|
|
4
|
+
types?: {
|
|
5
|
+
extendsFrom?: InterfaceData[];
|
|
6
|
+
generic?: InterfaceData[];
|
|
7
|
+
};
|
|
8
|
+
templates?: {
|
|
9
|
+
service?: {
|
|
10
|
+
path: string;
|
|
11
|
+
options?: Partial<ServiceTemplateParams>;
|
|
12
|
+
transform?: (options: ServiceTemplateParams) => string;
|
|
13
|
+
content?: string;
|
|
14
|
+
httpParamsHandler?: string;
|
|
15
|
+
httpParamsHandlerImport?: string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
input?: string;
|
|
19
|
+
output?: string;
|
|
20
|
+
groupingMode?: 'tags' | 'path';
|
|
21
|
+
segmentsToIgnore?: string[];
|
|
22
|
+
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { SwaggularConfig } from './config';
|
|
1
2
|
export type ParsedArgs = {
|
|
2
3
|
args: Record<string, string | boolean>;
|
|
3
4
|
positional: string[];
|
|
4
5
|
};
|
|
5
6
|
export interface ArgsVariables {
|
|
6
|
-
groupingMode: 'tags' | 'path';
|
|
7
|
-
segmentsToIgnore: string[];
|
|
8
7
|
input: string;
|
|
9
8
|
output: string;
|
|
10
9
|
noGenerate: boolean;
|
|
10
|
+
groupingMode: 'tags' | 'path';
|
|
11
|
+
segmentsToIgnore: string[];
|
|
12
|
+
templates?: SwaggularConfig['templates'];
|
|
13
|
+
types?: SwaggularConfig['types'];
|
|
11
14
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface ServiceTemplateParams {
|
|
2
|
+
name: string;
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
methods: string;
|
|
5
|
+
imports: string;
|
|
6
|
+
hasHttpParamsHandler: boolean;
|
|
7
|
+
httpParamsHandler?: string;
|
|
8
|
+
httpParamsHandlerImport?: string;
|
|
9
|
+
modelImports?: string;
|
|
10
|
+
extraAngularImports?: string;
|
|
11
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildTypes = buildTypes;
|
|
4
4
|
exports.switchTypeJson = switchTypeJson;
|
|
5
|
+
const interface_state_1 = require("../core/state/interface-state");
|
|
5
6
|
function buildTypes(masterSchema, defaultType = 'any') {
|
|
6
7
|
if (!masterSchema)
|
|
7
8
|
return defaultType;
|
|
@@ -18,12 +19,8 @@ function buildTypes(masterSchema, defaultType = 'any') {
|
|
|
18
19
|
return defaultType;
|
|
19
20
|
const ref = schema?.['$ref'];
|
|
20
21
|
if (ref) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
if (isPagedResultDto) {
|
|
24
|
-
return `PagedResultDto<${iface.replace('PagedResultDto', '')}>`;
|
|
25
|
-
}
|
|
26
|
-
return iface;
|
|
22
|
+
const name = ref.split('/').pop();
|
|
23
|
+
return interface_state_1.interfaceState.getTypeMapping(name) || name;
|
|
27
24
|
}
|
|
28
25
|
return switchTypeJson(schema);
|
|
29
26
|
}
|
|
@@ -34,12 +31,8 @@ function switchTypeJson(schema) {
|
|
|
34
31
|
const type = schema?.['type'];
|
|
35
32
|
const format = schema?.['format'];
|
|
36
33
|
if (ref) {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
if (isPagedResultDto) {
|
|
40
|
-
return `PagedResultDto<${iface.replace('PagedResultDto', '')}>`;
|
|
41
|
-
}
|
|
42
|
-
return iface;
|
|
34
|
+
const name = ref.split('/').pop();
|
|
35
|
+
return interface_state_1.interfaceState.getTypeMapping(name) || name;
|
|
43
36
|
}
|
|
44
37
|
if (type === 'object') {
|
|
45
38
|
return 'any';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.loadConfig = loadConfig;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
function loadConfig(configPath) {
|
|
40
|
+
const defaultConfigPath = 'swaggular.config.json';
|
|
41
|
+
const resolvedPath = path.resolve(configPath || defaultConfigPath);
|
|
42
|
+
if (fs.existsSync(resolvedPath)) {
|
|
43
|
+
try {
|
|
44
|
+
const fileContent = fs.readFileSync(resolvedPath, 'utf-8');
|
|
45
|
+
return JSON.parse(fileContent);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.error(`Error parsing config file at ${resolvedPath}:`, error);
|
|
49
|
+
return {};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function computeLocations(): Record<string, string[]>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeLocations = computeLocations;
|
|
4
|
+
const swagger_state_1 = require("../core/state/swagger-state");
|
|
5
|
+
const string_utils_1 = require("./string-utils");
|
|
6
|
+
const path_utils_1 = require("./path-utils");
|
|
7
|
+
function computeLocations() {
|
|
8
|
+
const swagger = swagger_state_1.swaggerState.getSwagger();
|
|
9
|
+
const groupedPaths = swagger_state_1.swaggerState.getPathsGroupedByScope();
|
|
10
|
+
const locations = {};
|
|
11
|
+
if (!swagger || !swagger.paths) {
|
|
12
|
+
return locations;
|
|
13
|
+
}
|
|
14
|
+
const processSchema = (dtoName, location) => {
|
|
15
|
+
if (locations[dtoName])
|
|
16
|
+
return;
|
|
17
|
+
if (isEnum(dtoName))
|
|
18
|
+
return;
|
|
19
|
+
locations[dtoName] = location;
|
|
20
|
+
const schemas = swagger_state_1.swaggerState.getSchemas();
|
|
21
|
+
if (!schemas || !schemas[dtoName])
|
|
22
|
+
return;
|
|
23
|
+
const schema = schemas[dtoName];
|
|
24
|
+
const refs = getReferences(schema);
|
|
25
|
+
for (const ref of refs) {
|
|
26
|
+
const childName = ref.split('/').pop();
|
|
27
|
+
if (childName) {
|
|
28
|
+
processSchema(childName, location);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
if (groupedPaths) {
|
|
33
|
+
for (const [groupName, group] of Object.entries(groupedPaths)) {
|
|
34
|
+
if (group.baseSegments && group.baseSegments.length > 0) {
|
|
35
|
+
const folder = (0, string_utils_1.toKebabCase)(group.baseSegments[0]);
|
|
36
|
+
if (!locations[groupName]) {
|
|
37
|
+
locations[groupName] = [folder];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// 1. Map Generated Parameter Interfaces
|
|
43
|
+
if (groupedPaths) {
|
|
44
|
+
for (const groupedPath of Object.values(groupedPaths)) {
|
|
45
|
+
for (const innerPath of groupedPath.paths) {
|
|
46
|
+
const pathInfo = swagger.paths[innerPath];
|
|
47
|
+
if (!pathInfo)
|
|
48
|
+
continue;
|
|
49
|
+
const methods = ['get', 'post', 'put', 'delete', 'patch'];
|
|
50
|
+
for (const method of methods) {
|
|
51
|
+
const operation = pathInfo[method];
|
|
52
|
+
if (!operation)
|
|
53
|
+
continue;
|
|
54
|
+
// Check if parameters exist (similar condition to generate-interface)
|
|
55
|
+
if (operation.parameters && operation.parameters.length > 0) {
|
|
56
|
+
const interfaceName = (0, path_utils_1.computeParametersName)(method, innerPath, groupedPath);
|
|
57
|
+
if (operation.tags && operation.tags.length > 0) {
|
|
58
|
+
const tag = operation.tags[0];
|
|
59
|
+
const folder = (0, string_utils_1.toKebabCase)(tag);
|
|
60
|
+
if (!locations[interfaceName]) {
|
|
61
|
+
locations[interfaceName] = [folder];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// 2. Map DTOs and Services
|
|
70
|
+
for (const pathItem of Object.values(swagger.paths)) {
|
|
71
|
+
if (!pathItem)
|
|
72
|
+
continue;
|
|
73
|
+
const operations = [
|
|
74
|
+
pathItem.get,
|
|
75
|
+
pathItem.put,
|
|
76
|
+
pathItem.post,
|
|
77
|
+
pathItem.delete,
|
|
78
|
+
pathItem.options,
|
|
79
|
+
pathItem.head,
|
|
80
|
+
pathItem.patch,
|
|
81
|
+
pathItem.trace,
|
|
82
|
+
];
|
|
83
|
+
for (const operation of operations) {
|
|
84
|
+
if (!operation || !operation.tags || operation.tags.length === 0) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const tag = operation.tags[0];
|
|
88
|
+
const folder = (0, string_utils_1.toKebabCase)(tag);
|
|
89
|
+
const location = [folder];
|
|
90
|
+
// Map Service (Tag) to location
|
|
91
|
+
if (!locations[tag]) {
|
|
92
|
+
locations[tag] = location;
|
|
93
|
+
}
|
|
94
|
+
// Map DTOs used in this operation
|
|
95
|
+
const refs = getReferences(operation);
|
|
96
|
+
for (const ref of refs) {
|
|
97
|
+
const dtoName = ref.split('/').pop();
|
|
98
|
+
if (dtoName) {
|
|
99
|
+
processSchema(dtoName, location);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return locations;
|
|
105
|
+
}
|
|
106
|
+
function isEnum(name) {
|
|
107
|
+
const schemas = swagger_state_1.swaggerState.getSchemas();
|
|
108
|
+
if (!schemas)
|
|
109
|
+
return false;
|
|
110
|
+
const schema = schemas[name];
|
|
111
|
+
if (!schema)
|
|
112
|
+
return false;
|
|
113
|
+
if ('$ref' in schema)
|
|
114
|
+
return false;
|
|
115
|
+
return !!schema.enum;
|
|
116
|
+
}
|
|
117
|
+
function getReferences(obj) {
|
|
118
|
+
const refs = [];
|
|
119
|
+
if (!obj || typeof obj !== 'object') {
|
|
120
|
+
return refs;
|
|
121
|
+
}
|
|
122
|
+
if (Array.isArray(obj)) {
|
|
123
|
+
for (const item of obj) {
|
|
124
|
+
refs.push(...getReferences(item));
|
|
125
|
+
}
|
|
126
|
+
return refs;
|
|
127
|
+
}
|
|
128
|
+
for (const key of Object.keys(obj)) {
|
|
129
|
+
if (key === '$ref' && typeof obj[key] === 'string') {
|
|
130
|
+
refs.push(obj[key]);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
refs.push(...getReferences(obj[key]));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return refs;
|
|
137
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { GroupedPath } from '../types/grouped-paths';
|
|
1
2
|
export declare function isVariable(segment: string): boolean;
|
|
2
3
|
export declare function getExtraSegments(fullPath: string, baseUrl: string): string[];
|
|
3
4
|
export declare function removeVariablesFromPath(path: string): string;
|
|
4
5
|
export declare function createBaseUrl(baseSegments: string[]): string;
|
|
5
6
|
export declare function findCommonBaseUrl(paths: string[]): string;
|
|
6
7
|
export declare function standarizedPath(path: string): string;
|
|
8
|
+
export declare function computeParametersName(method: string, innerPath: string, groupedPath: GroupedPath): string;
|
package/dist/utils/path-utils.js
CHANGED
|
@@ -6,6 +6,8 @@ exports.removeVariablesFromPath = removeVariablesFromPath;
|
|
|
6
6
|
exports.createBaseUrl = createBaseUrl;
|
|
7
7
|
exports.findCommonBaseUrl = findCommonBaseUrl;
|
|
8
8
|
exports.standarizedPath = standarizedPath;
|
|
9
|
+
exports.computeParametersName = computeParametersName;
|
|
10
|
+
const string_utils_1 = require("./string-utils");
|
|
9
11
|
function isVariable(segment) {
|
|
10
12
|
return /^\{.+\}$/.test(segment);
|
|
11
13
|
}
|
|
@@ -63,3 +65,21 @@ function standarizedPath(path) {
|
|
|
63
65
|
const filtered = splitted.filter((p) => p !== '').map((p) => p.toLowerCase());
|
|
64
66
|
return filtered.join('/');
|
|
65
67
|
}
|
|
68
|
+
function computeParametersName(method, innerPath, groupedPath) {
|
|
69
|
+
const dict = {
|
|
70
|
+
get: '',
|
|
71
|
+
post: 'Create',
|
|
72
|
+
put: 'Update',
|
|
73
|
+
delete: 'Delete',
|
|
74
|
+
patch: 'Patch',
|
|
75
|
+
};
|
|
76
|
+
const name = dict[method];
|
|
77
|
+
const extra = (0, string_utils_1.kebabToPascalCase)(removeVariablesFromPath(getExtraSegments(innerPath, createBaseUrl(groupedPath.baseSegments)).join('')));
|
|
78
|
+
// Avoid duplication if extra starts with groupName
|
|
79
|
+
let suffix = extra;
|
|
80
|
+
const groupName = groupedPath.groupName;
|
|
81
|
+
if (suffix.startsWith(groupName)) {
|
|
82
|
+
suffix = suffix.substring(groupName.length);
|
|
83
|
+
}
|
|
84
|
+
return name + groupName + (0, string_utils_1.upFirst)(suffix) + 'Params';
|
|
85
|
+
}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
export declare function kebabToCamelCase(str: string): string;
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
4
|
-
* @param str :
|
|
5
|
-
* @returns
|
|
6
|
-
*/
|
|
7
2
|
export declare function kebabToPascalCase(str: string): string;
|
|
8
3
|
export declare function removeAllWhitespace(text: string): string;
|
|
9
4
|
export declare function lowerFirst(str: string): string;
|
|
@@ -11,11 +11,6 @@ exports.toPascalCase = toPascalCase;
|
|
|
11
11
|
function kebabToCamelCase(str) {
|
|
12
12
|
return str.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
16
|
-
* @param str :
|
|
17
|
-
* @returns
|
|
18
|
-
*/
|
|
19
14
|
function kebabToPascalCase(str) {
|
|
20
15
|
const camel = str.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
21
16
|
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.renderServiceTemplate = renderServiceTemplate;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
function renderServiceTemplate(templatePath, params) {
|
|
40
|
+
// 1. Read the template file
|
|
41
|
+
const absolutePath = path.resolve(templatePath);
|
|
42
|
+
let templateContent;
|
|
43
|
+
try {
|
|
44
|
+
if (fs.existsSync(absolutePath)) {
|
|
45
|
+
templateContent = fs.readFileSync(absolutePath, 'utf-8');
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.error(`Template file not found at ${absolutePath}`);
|
|
49
|
+
return '';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
console.error(`Error reading template file at ${absolutePath}`, err);
|
|
54
|
+
return '';
|
|
55
|
+
}
|
|
56
|
+
// 2. Perform replacements
|
|
57
|
+
// Simple mustache-like replacement: {{variable}}
|
|
58
|
+
// We can use a regex or simple replaceAll if available (Node 15+) or split/join.
|
|
59
|
+
let result = templateContent;
|
|
60
|
+
// Replace known params
|
|
61
|
+
result = result.replace(/{{name}}/g, params.name);
|
|
62
|
+
result = result.replace(/{{baseUrl}}/g, params.baseUrl);
|
|
63
|
+
result = result.replace(/{{methods}}/g, params.methods);
|
|
64
|
+
result = result.replace(/{{modelImports}}/g, params.modelImports || '');
|
|
65
|
+
result = result.replace(/{{imports}}/g, params.imports);
|
|
66
|
+
// Handle conditional logic for httpParamsHandlerImport
|
|
67
|
+
result = result.replace(/{{httpParamsHandlerImport}}/g, params.httpParamsHandlerImport || '');
|
|
68
|
+
result = result.replace(/{{extraAngularImports}}/g, params.extraAngularImports || '');
|
|
69
|
+
// Cleanup excessive newlines (3 or more, potentially with spaces) to just 2
|
|
70
|
+
result = result.replace(/(\r?\n\s*){3,}/g, '\n\n');
|
|
71
|
+
return result;
|
|
72
|
+
}
|