@ygorazambuja/sauron 1.0.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/README.md +570 -0
- package/bin.ts +15 -0
- package/docs/plugins.md +154 -0
- package/package.json +59 -0
- package/src/cli/args.ts +196 -0
- package/src/cli/config.ts +228 -0
- package/src/cli/main.ts +276 -0
- package/src/cli/project.ts +113 -0
- package/src/cli/types.ts +22 -0
- package/src/generators/angular.ts +76 -0
- package/src/generators/fetch.ts +994 -0
- package/src/generators/missing-definitions.ts +776 -0
- package/src/generators/type-coverage.ts +938 -0
- package/src/index.ts +47 -0
- package/src/plugins/builtin/angular.ts +146 -0
- package/src/plugins/builtin/axios.ts +307 -0
- package/src/plugins/builtin/fetch.ts +140 -0
- package/src/plugins/registry.ts +84 -0
- package/src/plugins/runner.ts +174 -0
- package/src/plugins/types.ts +97 -0
- package/src/schemas/swagger.ts +134 -0
- package/src/utils/index.ts +1599 -0
package/src/cli/main.ts
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
createModelsWithOperationTypes,
|
|
5
|
+
fetchJsonFromUrl,
|
|
6
|
+
readJsonFile,
|
|
7
|
+
verifySwaggerComposition,
|
|
8
|
+
} from "../utils";
|
|
9
|
+
import { runHttpPlugins } from "../plugins/runner";
|
|
10
|
+
import { parseArgs, parseCommand, showHelp } from "./args";
|
|
11
|
+
import {
|
|
12
|
+
createGeneratedFileHeader,
|
|
13
|
+
formatGeneratedFile,
|
|
14
|
+
initConfigFile,
|
|
15
|
+
loadSauronConfig,
|
|
16
|
+
mergeOptionsWithConfig,
|
|
17
|
+
} from "./config";
|
|
18
|
+
import { isAngularProject, resolveOutputBasePath } from "./project";
|
|
19
|
+
import { DEFAULT_CONFIG_FILE, type CliOptions } from "./types";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Main.
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* main();
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export async function main() {
|
|
29
|
+
const command = parseCommand();
|
|
30
|
+
const cliOptions = parseArgs();
|
|
31
|
+
|
|
32
|
+
if (cliOptions.help) {
|
|
33
|
+
showHelp();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (command === "init") {
|
|
38
|
+
await initConfigFile(cliOptions.config || DEFAULT_CONFIG_FILE);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let options = cliOptions;
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const loadedConfig = await loadSauronConfig(
|
|
46
|
+
options.config || DEFAULT_CONFIG_FILE,
|
|
47
|
+
);
|
|
48
|
+
if (loadedConfig) {
|
|
49
|
+
options = mergeOptionsWithConfig(cliOptions, loadedConfig);
|
|
50
|
+
console.log(
|
|
51
|
+
`āļø Using config file: ${options.config || DEFAULT_CONFIG_FILE}`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let config: unknown;
|
|
56
|
+
if (options.url) {
|
|
57
|
+
console.log(`š Downloading OpenAPI spec from: ${options.url}`);
|
|
58
|
+
config = await fetchJsonFromUrl(options.url);
|
|
59
|
+
}
|
|
60
|
+
if (!options.url) {
|
|
61
|
+
console.log(`š Reading OpenAPI spec from: ${options.input}`);
|
|
62
|
+
config = await readJsonFile(options.input);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (typeof config !== "object") {
|
|
66
|
+
throw new Error("Config is not an object");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log("ā
Validating OpenAPI schema...");
|
|
70
|
+
const schema = verifySwaggerComposition(config as Record<string, unknown>);
|
|
71
|
+
|
|
72
|
+
const requestedPluginIds = resolveEffectivePluginIds(options);
|
|
73
|
+
logPluginCompatibilityNotice(options, requestedPluginIds);
|
|
74
|
+
|
|
75
|
+
const angularDetected = isAngularProject();
|
|
76
|
+
const preferAngularOutput = requestedPluginIds.includes("angular");
|
|
77
|
+
const baseOutputPath = resolveOutputBasePath(options, preferAngularOutput);
|
|
78
|
+
const modelsPath = join(baseOutputPath, "models", "index.ts");
|
|
79
|
+
mkdirSync(dirname(modelsPath), { recursive: true });
|
|
80
|
+
|
|
81
|
+
const fileHeader = createGeneratedFileHeader(schema);
|
|
82
|
+
|
|
83
|
+
console.log("š§ Generating TypeScript models...");
|
|
84
|
+
const { models, operationTypes, typeNameMap } =
|
|
85
|
+
createModelsWithOperationTypes(schema);
|
|
86
|
+
const formattedModels = await formatGeneratedFile(
|
|
87
|
+
`${fileHeader}\n${models.join("\n")}`,
|
|
88
|
+
modelsPath,
|
|
89
|
+
);
|
|
90
|
+
writeFileSync(modelsPath, formattedModels);
|
|
91
|
+
|
|
92
|
+
const pluginResults = await runHttpPlugins(requestedPluginIds, {
|
|
93
|
+
schema,
|
|
94
|
+
options,
|
|
95
|
+
baseOutputPath,
|
|
96
|
+
modelsPath,
|
|
97
|
+
fileHeader,
|
|
98
|
+
operationTypes,
|
|
99
|
+
typeNameMap,
|
|
100
|
+
isAngularProject: angularDetected,
|
|
101
|
+
writeFormattedFile: async (filePath: string, content: string) => {
|
|
102
|
+
const formattedContent = await formatGeneratedFile(content, filePath);
|
|
103
|
+
writeFileSync(filePath, formattedContent);
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
logPluginReports(pluginResults);
|
|
108
|
+
|
|
109
|
+
console.log("\nā
Generation complete!");
|
|
110
|
+
console.log(`š Models: ${models.length} TypeScript interfaces/types`);
|
|
111
|
+
logPluginMethodSummary(pluginResults);
|
|
112
|
+
console.log(`š Output: ${resolveOutputDisplayPath(options, angularDetected, preferAngularOutput)}`);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error("ā Error:", error);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Resolve effective plugin IDs.
|
|
121
|
+
* @param options Input parameter `options`.
|
|
122
|
+
* @returns Resolve effective plugin IDs output as `string[]`.
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* const result = resolveEffectivePluginIds({
|
|
126
|
+
* input: "swagger.json",
|
|
127
|
+
* angular: false,
|
|
128
|
+
* http: true,
|
|
129
|
+
* help: false,
|
|
130
|
+
* });
|
|
131
|
+
* // result: string[]
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
function resolveEffectivePluginIds(options: CliOptions): string[] {
|
|
135
|
+
if (options.plugin && options.plugin.length > 0) {
|
|
136
|
+
return normalizePluginIds(options.plugin);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!options.http) {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (options.angular) {
|
|
144
|
+
return ["angular"];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return ["fetch"];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Normalize plugin IDs.
|
|
152
|
+
* @param pluginIds Input parameter `pluginIds`.
|
|
153
|
+
* @returns Normalize plugin IDs output as `string[]`.
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* const result = normalizePluginIds(["fetch", "Angular"]);
|
|
157
|
+
* // result: string[]
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
function normalizePluginIds(pluginIds: string[]): string[] {
|
|
161
|
+
const normalizedIds: string[] = [];
|
|
162
|
+
const usedIds = new Set<string>();
|
|
163
|
+
|
|
164
|
+
for (const pluginId of pluginIds) {
|
|
165
|
+
const normalizedId = pluginId.trim().toLowerCase();
|
|
166
|
+
if (!normalizedId) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (usedIds.has(normalizedId)) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
usedIds.add(normalizedId);
|
|
173
|
+
normalizedIds.push(normalizedId);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return normalizedIds;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Log plugin compatibility notice.
|
|
181
|
+
* @param options Input parameter `options`.
|
|
182
|
+
* @param effectivePluginIds Input parameter `effectivePluginIds`.
|
|
183
|
+
* @example
|
|
184
|
+
* ```ts
|
|
185
|
+
* logPluginCompatibilityNotice(
|
|
186
|
+
* { input: "swagger.json", angular: true, http: true, help: false, plugin: ["fetch"] },
|
|
187
|
+
* ["fetch"],
|
|
188
|
+
* );
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
function logPluginCompatibilityNotice(
|
|
192
|
+
options: CliOptions,
|
|
193
|
+
effectivePluginIds: string[],
|
|
194
|
+
): void {
|
|
195
|
+
if (!options.plugin || options.plugin.length === 0) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (!options.http && !options.angular) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (effectivePluginIds.length === 0) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
console.log(
|
|
206
|
+
"ā¹ļø --plugin provided. Ignoring compatibility aliases --http/--angular.",
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Log plugin reports.
|
|
212
|
+
* @param pluginResults Input parameter `pluginResults`.
|
|
213
|
+
* @example
|
|
214
|
+
* ```ts
|
|
215
|
+
* logPluginReports([]);
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
function logPluginReports(
|
|
219
|
+
pluginResults: Array<{ reportPath: string; typeCoverageReportPath?: string }>,
|
|
220
|
+
): void {
|
|
221
|
+
for (const result of pluginResults) {
|
|
222
|
+
console.log(`š§¾ Missing Swagger definitions report: ${result.reportPath}`);
|
|
223
|
+
if (!result.typeCoverageReportPath) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
console.log(`š Type coverage report: ${result.typeCoverageReportPath}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Log plugin method summary.
|
|
232
|
+
* @param pluginResults Input parameter `pluginResults`.
|
|
233
|
+
* @example
|
|
234
|
+
* ```ts
|
|
235
|
+
* logPluginMethodSummary([]);
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
function logPluginMethodSummary(
|
|
239
|
+
pluginResults: Array<{ executedPluginId: string; methodCount: number }>,
|
|
240
|
+
): void {
|
|
241
|
+
for (const result of pluginResults) {
|
|
242
|
+
console.log(
|
|
243
|
+
`š HTTP Methods (${result.executedPluginId}): ${result.methodCount} methods`,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Resolve output display path.
|
|
250
|
+
* @param options Input parameter `options`.
|
|
251
|
+
* @param angularDetected Input parameter `angularDetected`.
|
|
252
|
+
* @param preferAngularOutput Input parameter `preferAngularOutput`.
|
|
253
|
+
* @returns Resolve output display path output as `string`.
|
|
254
|
+
* @example
|
|
255
|
+
* ```ts
|
|
256
|
+
* const result = resolveOutputDisplayPath(
|
|
257
|
+
* { input: "swagger.json", angular: false, http: false, help: false },
|
|
258
|
+
* false,
|
|
259
|
+
* false,
|
|
260
|
+
* );
|
|
261
|
+
* // result: string
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
function resolveOutputDisplayPath(
|
|
265
|
+
options: CliOptions,
|
|
266
|
+
angularDetected: boolean,
|
|
267
|
+
preferAngularOutput: boolean,
|
|
268
|
+
): string {
|
|
269
|
+
if (options.output) {
|
|
270
|
+
return options.output;
|
|
271
|
+
}
|
|
272
|
+
if (preferAngularOutput && angularDetected) {
|
|
273
|
+
return "src/app/sauron";
|
|
274
|
+
}
|
|
275
|
+
return "outputs";
|
|
276
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import type { CliOptions } from "./types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Is angular project.
|
|
7
|
+
* @returns Is angular project output as `boolean`.
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const result = isAngularProject();
|
|
11
|
+
* // result: boolean
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function isAngularProject(): boolean {
|
|
15
|
+
if (existsSync("angular.json")) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const packageJsonPath = join(process.cwd(), "package.json");
|
|
21
|
+
if (existsSync(packageJsonPath)) {
|
|
22
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
23
|
+
const deps = {
|
|
24
|
+
...packageJson.dependencies,
|
|
25
|
+
...packageJson.devDependencies,
|
|
26
|
+
};
|
|
27
|
+
return "@angular/core" in deps;
|
|
28
|
+
}
|
|
29
|
+
} catch (_error) {
|
|
30
|
+
// Ignore errors when checking package.json
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get output paths.
|
|
38
|
+
* @param options Input parameter `options`.
|
|
39
|
+
* @returns Get output paths output as `unknown`.
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const result = getOutputPaths({});
|
|
43
|
+
* // result: unknown
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function getOutputPaths(options: CliOptions): {
|
|
47
|
+
modelsPath: string;
|
|
48
|
+
servicePath: string | undefined;
|
|
49
|
+
} {
|
|
50
|
+
const basePath = resolveOutputBasePath(options, options.angular);
|
|
51
|
+
|
|
52
|
+
mkdirSync(join(basePath, "models"), { recursive: true });
|
|
53
|
+
|
|
54
|
+
let servicePath: string;
|
|
55
|
+
if (options.http) {
|
|
56
|
+
const serviceDir =
|
|
57
|
+
options.angular && isAngularProject()
|
|
58
|
+
? "angular-http-client"
|
|
59
|
+
: "http-client";
|
|
60
|
+
mkdirSync(join(basePath, serviceDir), { recursive: true });
|
|
61
|
+
|
|
62
|
+
const serviceFileName =
|
|
63
|
+
options.angular && isAngularProject()
|
|
64
|
+
? "sauron-api.service.ts"
|
|
65
|
+
: "sauron-api.client.ts";
|
|
66
|
+
|
|
67
|
+
servicePath = join(basePath, serviceDir, serviceFileName);
|
|
68
|
+
} else {
|
|
69
|
+
servicePath = "";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
modelsPath: join(basePath, "models", "index.ts"),
|
|
74
|
+
servicePath,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Resolve output base path.
|
|
80
|
+
* @param options Input parameter `options`.
|
|
81
|
+
* @param preferAngularOutput Input parameter `preferAngularOutput`.
|
|
82
|
+
* @returns Resolve output base path output as `string`.
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* const result = resolveOutputBasePath(
|
|
86
|
+
* { input: "swagger.json", angular: false, http: false, help: false },
|
|
87
|
+
* false,
|
|
88
|
+
* );
|
|
89
|
+
* // result: string
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export function resolveOutputBasePath(
|
|
93
|
+
options: CliOptions,
|
|
94
|
+
preferAngularOutput: boolean,
|
|
95
|
+
): string {
|
|
96
|
+
if (options.output) {
|
|
97
|
+
return resolve(options.output);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const angularDetected = isAngularProject();
|
|
101
|
+
if (preferAngularOutput && angularDetected) {
|
|
102
|
+
console.log("ā
Angular project detected! Generating in src/app/sauron/");
|
|
103
|
+
return "src/app/sauron";
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (options.angular && !angularDetected) {
|
|
107
|
+
console.warn(
|
|
108
|
+
"ā ļø --angular flag used but Angular project not detected. Generating in outputs/ instead.",
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return "outputs";
|
|
113
|
+
}
|
package/src/cli/types.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface CliOptions {
|
|
2
|
+
input: string;
|
|
3
|
+
url?: string;
|
|
4
|
+
angular: boolean;
|
|
5
|
+
http: boolean;
|
|
6
|
+
plugin?: string[];
|
|
7
|
+
output?: string;
|
|
8
|
+
config?: string;
|
|
9
|
+
help: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SauronConfig {
|
|
13
|
+
input?: string;
|
|
14
|
+
url?: string;
|
|
15
|
+
angular?: boolean;
|
|
16
|
+
http?: boolean;
|
|
17
|
+
plugin?: string[];
|
|
18
|
+
output?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const DEFAULT_CONFIG_FILE = "sauron.config.ts";
|
|
22
|
+
export const DEFAULT_SAURON_VERSION = "1.0.0";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate angular service.
|
|
3
|
+
* @param methods Input parameter `methods`.
|
|
4
|
+
* @param imports Input parameter `imports`.
|
|
5
|
+
* @param _isAngularProject Input parameter `_isAngularProject`.
|
|
6
|
+
* @param paramsInterfaces Input parameter `paramsInterfaces`.
|
|
7
|
+
* @returns Generate angular service output as `string`.
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const result = generateAngularService([], [], true, []);
|
|
11
|
+
* // result: string
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function generateAngularService(
|
|
15
|
+
methods: string[],
|
|
16
|
+
imports: string[],
|
|
17
|
+
_isAngularProject: boolean,
|
|
18
|
+
paramsInterfaces: string[] = [],
|
|
19
|
+
): string {
|
|
20
|
+
const importStatement = buildModelImportStatement(imports);
|
|
21
|
+
const interfacesBlock = buildInterfacesBlock(paramsInterfaces);
|
|
22
|
+
const methodsBlock = methods.join("\n\n");
|
|
23
|
+
|
|
24
|
+
const serviceTemplate = `import { Injectable, inject } from "@angular/core";
|
|
25
|
+
import { HttpClient } from "@angular/common/http";
|
|
26
|
+
import { Observable } from "rxjs";
|
|
27
|
+
|
|
28
|
+
${importStatement}\n${interfacesBlock}\n\n
|
|
29
|
+
@Injectable({
|
|
30
|
+
providedIn: "root"
|
|
31
|
+
})
|
|
32
|
+
export class SauronApiService {
|
|
33
|
+
private readonly httpClient = inject(HttpClient);
|
|
34
|
+
|
|
35
|
+
${methodsBlock}
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
return serviceTemplate;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Build model import statement.
|
|
44
|
+
* @param imports Input parameter `imports`.
|
|
45
|
+
* @returns Build model import statement output as `string`.
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const result = buildModelImportStatement([]);
|
|
49
|
+
* // result: string
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
function buildModelImportStatement(imports: string[]): string {
|
|
53
|
+
if (imports.length === 0) {
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
const importList = imports.join(", ");
|
|
57
|
+
const importPath = "../models";
|
|
58
|
+
return `import { ${importList} } from "${importPath}";\n`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Build interfaces block.
|
|
63
|
+
* @param paramsInterfaces Input parameter `paramsInterfaces`.
|
|
64
|
+
* @returns Build interfaces block output as `string`.
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* const result = buildInterfacesBlock([]);
|
|
68
|
+
* // result: string
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
function buildInterfacesBlock(paramsInterfaces: string[]): string {
|
|
72
|
+
if (paramsInterfaces.length === 0) {
|
|
73
|
+
return "";
|
|
74
|
+
}
|
|
75
|
+
return `${paramsInterfaces.join("\n\n")}\n\n`;
|
|
76
|
+
}
|