@nestia/migrate 0.12.1 → 0.13.0-dev.20240411-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/lib/MigrateApplication.d.ts +3 -4
  2. package/lib/MigrateApplication.js +5157 -948
  3. package/lib/MigrateApplication.js.map +1 -1
  4. package/lib/analyzers/MigrateControllerAnalyzer.js +2 -1
  5. package/lib/analyzers/MigrateControllerAnalyzer.js.map +1 -1
  6. package/lib/analyzers/MigrateMethodAnalyzer.d.ts +2 -2
  7. package/lib/analyzers/MigrateMethodAnalyzer.js +60 -143
  8. package/lib/analyzers/MigrateMethodAnalyzer.js.map +1 -1
  9. package/lib/bundles/NEST_TEMPLATE.js +1 -1
  10. package/lib/factories/TypeLiteralFactory.js +3 -1
  11. package/lib/factories/TypeLiteralFactory.js.map +1 -1
  12. package/lib/internal/MigrateCommander.js +2 -2
  13. package/lib/internal/MigrateCommander.js.map +1 -1
  14. package/lib/module.d.ts +0 -6
  15. package/lib/module.js +0 -6
  16. package/lib/module.js.map +1 -1
  17. package/lib/programmers/MigrateApiFileProgrammer.d.ts +2 -2
  18. package/lib/programmers/MigrateApiFileProgrammer.js.map +1 -1
  19. package/lib/programmers/MigrateApiFunctionProgrammer.d.ts +3 -3
  20. package/lib/programmers/MigrateApiFunctionProgrammer.js.map +1 -1
  21. package/lib/programmers/MigrateApiNamespaceProgrammer.d.ts +2 -2
  22. package/lib/programmers/MigrateApiNamespaceProgrammer.js.map +1 -1
  23. package/lib/programmers/MigrateApiProgrammer.js +2 -2
  24. package/lib/programmers/MigrateApiProgrammer.js.map +1 -1
  25. package/lib/programmers/MigrateApiSimulatationProgrammer.d.ts +3 -3
  26. package/lib/programmers/MigrateApiSimulatationProgrammer.js.map +1 -1
  27. package/lib/programmers/MigrateApiStartProgrammer.js +8 -8
  28. package/lib/programmers/MigrateApiStartProgrammer.js.map +1 -1
  29. package/lib/programmers/MigrateDtoProgrammer.d.ts +2 -2
  30. package/lib/programmers/MigrateDtoProgrammer.js.map +1 -1
  31. package/lib/programmers/MigrateE2eFileProgrammer.d.ts +3 -3
  32. package/lib/programmers/MigrateE2eFileProgrammer.js.map +1 -1
  33. package/lib/programmers/MigrateE2eProgrammer.js +1 -1
  34. package/lib/programmers/MigrateE2eProgrammer.js.map +1 -1
  35. package/lib/programmers/MigrateNestControllerProgrammer.d.ts +2 -2
  36. package/lib/programmers/MigrateNestControllerProgrammer.js.map +1 -1
  37. package/lib/programmers/MigrateNestMethodProgrammer.d.ts +2 -2
  38. package/lib/programmers/MigrateNestMethodProgrammer.js.map +1 -1
  39. package/lib/programmers/MigrateNestProgrammer.js +2 -2
  40. package/lib/programmers/MigrateNestProgrammer.js.map +1 -1
  41. package/lib/programmers/MigrateSchemaProgrammer.d.ts +2 -3
  42. package/lib/programmers/MigrateSchemaProgrammer.js +81 -44
  43. package/lib/programmers/MigrateSchemaProgrammer.js.map +1 -1
  44. package/lib/structures/IMigrateDto.d.ts +2 -2
  45. package/lib/structures/IMigrateProgram.d.ts +3 -4
  46. package/lib/structures/IMigrateRoute.d.ts +6 -6
  47. package/lib/utils/OpenApiTypeChecker.d.ts +15 -0
  48. package/lib/utils/OpenApiTypeChecker.js +28 -0
  49. package/lib/utils/OpenApiTypeChecker.js.map +1 -0
  50. package/package.json +79 -78
  51. package/src/MigrateApplication.ts +8 -13
  52. package/src/analyzers/MigrateControllerAnalyzer.ts +7 -5
  53. package/src/analyzers/MigrateMethodAnalyzer.ts +103 -179
  54. package/src/bundles/NEST_TEMPLATE.ts +1 -1
  55. package/src/factories/TypeLiteralFactory.ts +8 -1
  56. package/src/internal/MigrateCommander.ts +4 -4
  57. package/src/module.ts +0 -6
  58. package/src/programmers/MigrateApiFileProgrammer.ts +2 -2
  59. package/src/programmers/MigrateApiFunctionProgrammer.ts +3 -3
  60. package/src/programmers/MigrateApiNamespaceProgrammer.ts +5 -5
  61. package/src/programmers/MigrateApiProgrammer.ts +4 -2
  62. package/src/programmers/MigrateApiSimulatationProgrammer.ts +4 -4
  63. package/src/programmers/MigrateApiStartProgrammer.ts +8 -8
  64. package/src/programmers/MigrateDtoProgrammer.ts +5 -6
  65. package/src/programmers/MigrateE2eFileProgrammer.ts +4 -4
  66. package/src/programmers/MigrateE2eProgrammer.ts +3 -3
  67. package/src/programmers/MigrateImportProgrammer.ts +117 -117
  68. package/src/programmers/MigrateNestControllerProgrammer.ts +2 -2
  69. package/src/programmers/MigrateNestMethodProgrammer.ts +6 -7
  70. package/src/programmers/MigrateNestProgrammer.ts +2 -2
  71. package/src/programmers/MigrateSchemaProgrammer.ts +102 -54
  72. package/src/structures/IMigrateDto.ts +2 -2
  73. package/src/structures/IMigrateProgram.ts +4 -4
  74. package/src/structures/IMigrateRoute.ts +6 -6
  75. package/src/utils/OpenApiTypeChecker.ts +73 -0
  76. package/lib/structures/ISwagger.d.ts +0 -18
  77. package/lib/structures/ISwagger.js +0 -3
  78. package/lib/structures/ISwagger.js.map +0 -1
  79. package/lib/structures/ISwaggerComponents.d.ts +0 -12
  80. package/lib/structures/ISwaggerComponents.js +0 -3
  81. package/lib/structures/ISwaggerComponents.js.map +0 -1
  82. package/lib/structures/ISwaggerInfo.d.ts +0 -71
  83. package/lib/structures/ISwaggerInfo.js +0 -3
  84. package/lib/structures/ISwaggerInfo.js.map +0 -1
  85. package/lib/structures/ISwaggerRoute.d.ts +0 -15
  86. package/lib/structures/ISwaggerRoute.js +0 -3
  87. package/lib/structures/ISwaggerRoute.js.map +0 -1
  88. package/lib/structures/ISwaggerRouteBodyContent.d.ts +0 -14
  89. package/lib/structures/ISwaggerRouteBodyContent.js +0 -3
  90. package/lib/structures/ISwaggerRouteBodyContent.js.map +0 -1
  91. package/lib/structures/ISwaggerRouteHeader.d.ts +0 -0
  92. package/lib/structures/ISwaggerRouteHeader.js +0 -2
  93. package/lib/structures/ISwaggerRouteHeader.js.map +0 -1
  94. package/lib/structures/ISwaggerRouteParameter.d.ts +0 -13
  95. package/lib/structures/ISwaggerRouteParameter.js +0 -3
  96. package/lib/structures/ISwaggerRouteParameter.js.map +0 -1
  97. package/lib/structures/ISwaggerRouteRequestBody.d.ts +0 -11
  98. package/lib/structures/ISwaggerRouteRequestBody.js +0 -3
  99. package/lib/structures/ISwaggerRouteRequestBody.js.map +0 -1
  100. package/lib/structures/ISwaggerRouteResponse.d.ts +0 -10
  101. package/lib/structures/ISwaggerRouteResponse.js +0 -3
  102. package/lib/structures/ISwaggerRouteResponse.js.map +0 -1
  103. package/lib/structures/ISwaggerSchema.d.ts +0 -76
  104. package/lib/structures/ISwaggerSchema.js +0 -3
  105. package/lib/structures/ISwaggerSchema.js.map +0 -1
  106. package/lib/structures/ISwaggerSecurityScheme.d.ts +0 -40
  107. package/lib/structures/ISwaggerSecurityScheme.js +0 -3
  108. package/lib/structures/ISwaggerSecurityScheme.js.map +0 -1
  109. package/lib/structures/ISwaggerV20.d.ts +0 -8
  110. package/lib/structures/ISwaggerV20.js +0 -3
  111. package/lib/structures/ISwaggerV20.js.map +0 -1
  112. package/lib/structures/ISwaggerV31.d.ts +0 -9
  113. package/lib/structures/ISwaggerV31.js +0 -3
  114. package/lib/structures/ISwaggerV31.js.map +0 -1
  115. package/lib/utils/OpenApiConverter.d.ts +0 -5
  116. package/lib/utils/OpenApiConverter.js +0 -1572
  117. package/lib/utils/OpenApiConverter.js.map +0 -1
  118. package/lib/utils/SwaggerComponentsExplorer.d.ts +0 -9
  119. package/lib/utils/SwaggerComponentsExplorer.js +0 -29
  120. package/lib/utils/SwaggerComponentsExplorer.js.map +0 -1
  121. package/lib/utils/SwaggerTypeChecker.d.ts +0 -16
  122. package/lib/utils/SwaggerTypeChecker.js +0 -34
  123. package/lib/utils/SwaggerTypeChecker.js.map +0 -1
  124. package/src/structures/ISwagger.ts +0 -23
  125. package/src/structures/ISwaggerComponents.ts +0 -13
  126. package/src/structures/ISwaggerInfo.ts +0 -80
  127. package/src/structures/ISwaggerRoute.ts +0 -20
  128. package/src/structures/ISwaggerRouteBodyContent.ts +0 -15
  129. package/src/structures/ISwaggerRouteHeader.ts +0 -0
  130. package/src/structures/ISwaggerRouteParameter.ts +0 -14
  131. package/src/structures/ISwaggerRouteRequestBody.ts +0 -12
  132. package/src/structures/ISwaggerRouteResponse.ts +0 -11
  133. package/src/structures/ISwaggerSchema.ts +0 -91
  134. package/src/structures/ISwaggerSecurityScheme.ts +0 -47
  135. package/src/structures/ISwaggerV20.ts +0 -10
  136. package/src/structures/ISwaggerV31.ts +0 -10
  137. package/src/utils/OpenApiConverter.ts +0 -19
  138. package/src/utils/SwaggerComponentsExplorer.ts +0 -43
  139. package/src/utils/SwaggerTypeChecker.ts +0 -67
@@ -1,71 +1,28 @@
1
- import typia from "typia";
1
+ import { OpenApi } from "@samchon/openapi";
2
2
  import { Escaper } from "typia/lib/utils/Escaper";
3
3
 
4
4
  import { IMigrateProgram } from "../structures/IMigrateProgram";
5
5
  import { IMigrateRoute } from "../structures/IMigrateRoute";
6
- import { ISwagger } from "../structures/ISwagger";
7
- import { ISwaggerRoute } from "../structures/ISwaggerRoute";
8
- import { ISwaggerRouteBodyContent } from "../structures/ISwaggerRouteBodyContent";
9
- import { ISwaggerRouteParameter } from "../structures/ISwaggerRouteParameter";
10
- import { ISwaggerRouteResponse } from "../structures/ISwaggerRouteResponse";
11
- import { ISwaggerSchema } from "../structures/ISwaggerSchema";
6
+ import { OpenApiTypeChecker } from "../utils/OpenApiTypeChecker";
12
7
  import { StringUtil } from "../utils/StringUtil";
13
- import { SwaggerComponentsExplorer } from "../utils/SwaggerComponentsExplorer";
14
- import { SwaggerTypeChecker } from "../utils/SwaggerTypeChecker";
15
8
 
16
9
  export namespace MigrateMethodAnalzyer {
17
10
  export const analyze =
18
11
  (props: Omit<IMigrateProgram.IProps, "dictionary">) =>
19
12
  (endpoint: { path: string; method: string }) =>
20
- (route: ISwaggerRoute): IMigrateRoute | null => {
13
+ (route: OpenApi.IOperation): IMigrateRoute | null => {
21
14
  const body = emplaceBodySchema("request")(
22
- emplaceReference(props.swagger)("body"),
15
+ emplaceReference(props.document)("body"),
23
16
  )(route.requestBody);
24
17
  const success = emplaceBodySchema("response")(
25
- emplaceReference(props.swagger)("response"),
18
+ emplaceReference(props.document)("response"),
26
19
  )(
27
- (() => {
28
- const response =
29
- route.responses?.["201"] ??
30
- route.responses?.["200"] ??
31
- route.responses?.default;
32
- if (response === undefined) return undefined;
33
- return (
34
- SwaggerComponentsExplorer.getResponse(props.swagger.components)(
35
- response,
36
- ) ?? undefined
37
- );
38
- })(),
20
+ route.responses?.["201"] ??
21
+ route.responses?.["200"] ??
22
+ route.responses?.default,
39
23
  );
40
24
 
41
25
  const failures: string[] = [];
42
- for (const p of route.parameters ?? [])
43
- if (
44
- SwaggerComponentsExplorer.getParameter(props.swagger.components)(
45
- p,
46
- ) === null
47
- )
48
- failures.push(
49
- `parameter "${(p as ISwaggerRouteParameter.IReference).$ref}" is not defined in "components.parameters".`,
50
- );
51
- for (const value of Object.values(route.responses ?? {}))
52
- if (
53
- SwaggerComponentsExplorer.getResponse(props.swagger.components)(
54
- value,
55
- ) === null
56
- )
57
- failures.push(
58
- `response "${(value as ISwaggerRouteResponse.IReference).$ref}" is not defined in "components.responses".`,
59
- );
60
- if (
61
- route.requestBody &&
62
- SwaggerComponentsExplorer.getRequestBody(props.swagger.components)(
63
- route.requestBody,
64
- ) === null
65
- )
66
- failures.push(
67
- `requestBody "${(route.requestBody as ISwaggerRouteParameter.IReference).$ref}" is not defined in "components.requestBodies".`,
68
- );
69
26
  if (body === false)
70
27
  failures.push(
71
28
  `supports only "application/json", "application/x-www-form-urlencoded", "multipart/form-data" and "text/plain" content type in the request body.`,
@@ -80,28 +37,18 @@ export namespace MigrateMethodAnalzyer {
80
37
  );
81
38
 
82
39
  const [headers, query] = ["header", "query"].map((type) => {
83
- const parameters: ISwaggerRouteParameter[] = (route.parameters ?? [])
84
- .filter(
85
- (p) =>
86
- SwaggerComponentsExplorer.getParameter(props.swagger.components)(
87
- p,
88
- )?.in === type,
89
- )
90
- .map(
91
- (p) =>
92
- SwaggerComponentsExplorer.getParameter(props.swagger.components)(
93
- p,
94
- )!,
95
- );
40
+ const parameters: OpenApi.IOperation.IParameter[] = (
41
+ route.parameters ?? []
42
+ ).filter((p) => p.in === type);
96
43
  if (parameters.length === 0) return null;
97
44
 
98
45
  const objects = parameters
99
46
  .map((p) =>
100
- SwaggerTypeChecker.isObject(p.schema)
47
+ OpenApiTypeChecker.isObject(p.schema)
101
48
  ? p.schema
102
- : SwaggerTypeChecker.isReference(p.schema) &&
103
- SwaggerTypeChecker.isObject(
104
- (props.swagger.components.schemas ?? {})[
49
+ : OpenApiTypeChecker.isReference(p.schema) &&
50
+ OpenApiTypeChecker.isObject(
51
+ props.document.components.schemas[
105
52
  p.schema.$ref.replace(`#/components/schemas/`, ``)
106
53
  ] ?? {},
107
54
  )
@@ -111,11 +58,11 @@ export namespace MigrateMethodAnalzyer {
111
58
  .filter((s) => !!s);
112
59
  const primitives = parameters.filter(
113
60
  (p) =>
114
- SwaggerTypeChecker.isBoolean(p.schema) ||
115
- SwaggerTypeChecker.isNumber(p.schema) ||
116
- SwaggerTypeChecker.isInteger(p.schema) ||
117
- SwaggerTypeChecker.isString(p.schema) ||
118
- SwaggerTypeChecker.isArray(p.schema),
61
+ OpenApiTypeChecker.isBoolean(p.schema) ||
62
+ OpenApiTypeChecker.isInteger(p.schema) ||
63
+ OpenApiTypeChecker.isNumber(p.schema) ||
64
+ OpenApiTypeChecker.isString(p.schema) ||
65
+ OpenApiTypeChecker.isArray(p.schema),
119
66
  );
120
67
  if (objects.length === 1 && primitives.length === 0) return objects[0];
121
68
  else if (objects.length > 1) {
@@ -125,23 +72,23 @@ export namespace MigrateMethodAnalzyer {
125
72
  return false;
126
73
  }
127
74
 
128
- const dto: ISwaggerSchema.IObject | null = objects[0]
129
- ? SwaggerTypeChecker.isObject(objects[0])
75
+ const dto: OpenApi.IJsonSchema.IObject | null = objects[0]
76
+ ? OpenApiTypeChecker.isObject(objects[0])
130
77
  ? objects[0]
131
- : ((props.swagger.components.schemas ?? {})[
132
- (objects[0] as ISwaggerSchema.IReference).$ref.replace(
78
+ : ((props.document.components.schemas ?? {})[
79
+ (objects[0] as OpenApi.IJsonSchema.IReference).$ref.replace(
133
80
  `#/components/schemas/`,
134
81
  ``,
135
82
  )
136
- ] as ISwaggerSchema.IObject)
83
+ ] as OpenApi.IJsonSchema.IObject)
137
84
  : null;
138
- const entire: ISwaggerSchema.IObject[] = [
85
+ const entire: OpenApi.IJsonSchema.IObject[] = [
139
86
  ...objects.map((o) =>
140
- SwaggerTypeChecker.isObject(o)
87
+ OpenApiTypeChecker.isObject(o)
141
88
  ? o
142
- : (props.swagger.components.schemas?.[
89
+ : (props.document.components.schemas?.[
143
90
  o.$ref.replace(`#/components/schemas/`, ``)
144
- ]! as ISwaggerSchema.IObject),
91
+ ]! as OpenApi.IJsonSchema.IObject),
145
92
  ),
146
93
  {
147
94
  type: "object",
@@ -163,14 +110,14 @@ export namespace MigrateMethodAnalzyer {
163
110
  ];
164
111
  return parameters.length === 0
165
112
  ? null
166
- : emplaceReference(props.swagger)(
113
+ : emplaceReference(props.document)(
167
114
  StringUtil.pascal(`I/Api/${endpoint.path}`) +
168
115
  "." +
169
116
  StringUtil.pascal(`${endpoint.method}/${type}`),
170
117
  )({
171
118
  type: "object",
172
119
  properties: Object.fromEntries([
173
- ...new Map<string, ISwaggerSchema>(
120
+ ...new Map<string, OpenApi.IJsonSchema>(
174
121
  entire
175
122
  .map((o) =>
176
123
  Object.entries(o.properties ?? {}).map(
@@ -181,7 +128,7 @@ export namespace MigrateMethodAnalzyer {
181
128
  ...schema,
182
129
  description:
183
130
  schema.description ?? schema.description,
184
- } as ISwaggerSchema,
131
+ } as OpenApi.IJsonSchema,
185
132
  ] as const,
186
133
  ),
187
134
  )
@@ -203,11 +150,7 @@ export namespace MigrateMethodAnalzyer {
203
150
  );
204
151
  if (
205
152
  parameterNames.length !==
206
- (route.parameters ?? []).filter(
207
- (p) =>
208
- SwaggerComponentsExplorer.getParameter(props.swagger.components)(p)
209
- ?.in === "path",
210
- ).length
153
+ route.parameters.filter((p) => p.in === "path").length
211
154
  )
212
155
  failures.push(
213
156
  "number of path parameters are not matched with its full path.",
@@ -233,11 +176,8 @@ export namespace MigrateMethodAnalzyer {
233
176
  schema: headers,
234
177
  }
235
178
  : null,
236
- parameters: (route.parameters ?? [])
237
- .map((p) =>
238
- SwaggerComponentsExplorer.getParameter(props.swagger.components)(p),
239
- )
240
- .filter((p) => p !== null && p.in === "path")
179
+ parameters: route.parameters
180
+ .filter((p) => p.in === "path")
241
181
  .map((p, i) => ({
242
182
  name: parameterNames[i],
243
183
  key: (() => {
@@ -265,105 +205,77 @@ export namespace MigrateMethodAnalzyer {
265
205
  exceptions: Object.fromEntries(
266
206
  Object.entries(route.responses ?? {})
267
207
  .filter(
268
- ([key, value]) =>
269
- key !== "200" &&
270
- key !== "201" &&
271
- key !== "default" &&
272
- !!SwaggerComponentsExplorer.getResponse(
273
- props.swagger.components,
274
- )(value)?.content?.["application/json"],
208
+ ([key]) => key !== "200" && key !== "201" && key !== "default",
275
209
  )
276
- .map(([key, value]) => {
277
- const r = SwaggerComponentsExplorer.getResponse(
278
- props.swagger.components,
279
- )(value)!;
280
- return [
281
- key,
282
- {
283
- description: r.description,
284
- schema: r.content?.["application/json"]?.schema ?? {},
285
- },
286
- ];
287
- }),
210
+ .map(([key, value]) => [
211
+ key,
212
+ {
213
+ description: value.description,
214
+ schema: value.content?.["application/json"]?.schema ?? {},
215
+ },
216
+ ]),
288
217
  ),
289
218
  deprecated: route.deprecated ?? false,
290
- comment: () => describe(props.swagger)(route),
219
+ comment: () => describe(route),
291
220
  tags: route.tags ?? [],
292
221
  };
293
222
  };
294
223
 
295
- const describe =
296
- (swagger: ISwagger) =>
297
- (route: ISwaggerRoute): string => {
298
- const commentTags: string[] = [];
299
- const add = (text: string) => {
300
- if (commentTags.every((line) => line !== text)) commentTags.push(text);
301
- };
302
-
303
- let description: string = route.description ?? "";
304
- if (route.summary) {
305
- const emended: string = route.summary.endsWith(".")
306
- ? route.summary
307
- : route.summary + ".";
308
- if (
309
- !!description.length &&
310
- !description.startsWith(route.summary) &&
311
- !route["x-nestia-jsDocTags"]?.some((t) => t.name === "summary")
312
- )
313
- description = `${emended}\n${description}`;
314
- }
315
- for (const p of route.parameters ?? []) {
316
- const param: ISwaggerRouteParameter | null = (() => {
317
- if (!typia.is<ISwaggerRouteParameter.IReference>(p))
318
- return typia.is<ISwaggerRouteParameter>(p) ? p : null;
319
- return (
320
- swagger.components.parameters?.[
321
- p.$ref.replace(`#/components/parameters/`, ``)
322
- ] ?? null
323
- );
324
- })();
325
- if (param !== null && param.description)
326
- add(`@param ${param.name} ${param.description}`);
327
- }
328
- if (route.requestBody?.description)
329
- add(`@param body ${route.requestBody.description}`);
330
- for (const security of route.security ?? [])
331
- for (const [name, scopes] of Object.entries(security))
332
- add(`@security ${[name, ...scopes].join("")}`);
333
- if (route.tags) route.tags.forEach((name) => add(`@tag ${name}`));
334
- if (route.deprecated) add("@deprecated");
335
- return description.length
336
- ? commentTags.length
337
- ? `${description}\n\n${commentTags.join("\n")}`
338
- : description
339
- : commentTags.join("\n");
224
+ const describe = (route: OpenApi.IOperation): string => {
225
+ const commentTags: string[] = [];
226
+ const add = (text: string) => {
227
+ if (commentTags.every((line) => line !== text)) commentTags.push(text);
340
228
  };
341
229
 
342
- const isNotObjectLiteral = (schema: ISwaggerSchema): boolean =>
343
- SwaggerTypeChecker.isReference(schema) ||
344
- SwaggerTypeChecker.isBoolean(schema) ||
345
- SwaggerTypeChecker.isNumber(schema) ||
346
- SwaggerTypeChecker.isString(schema) ||
347
- SwaggerTypeChecker.isUnknown(schema) ||
348
- (SwaggerTypeChecker.isAnyOf(schema) &&
349
- schema.anyOf.every(isNotObjectLiteral)) ||
350
- (SwaggerTypeChecker.isOneOf(schema) &&
230
+ let description: string = route.description ?? "";
231
+ if (route.summary) {
232
+ const emended: string = route.summary.endsWith(".")
233
+ ? route.summary
234
+ : route.summary + ".";
235
+ if (!!description.length && !description.startsWith(route.summary))
236
+ description = `${emended}\n${description}`;
237
+ }
238
+ for (const p of route.parameters)
239
+ if (p.description) add(`@param ${p.name} ${p.description}`);
240
+ if (route.requestBody?.description)
241
+ add(`@param body ${route.requestBody.description}`);
242
+ for (const security of route.security ?? [])
243
+ for (const [name, scopes] of Object.entries(security))
244
+ add(`@security ${[name, ...scopes].join("")}`);
245
+ if (route.tags) route.tags.forEach((name) => add(`@tag ${name}`));
246
+ if (route.deprecated) add("@deprecated");
247
+ return description.length
248
+ ? commentTags.length
249
+ ? `${description}\n\n${commentTags.join("\n")}`
250
+ : description
251
+ : commentTags.join("\n");
252
+ };
253
+
254
+ const isNotObjectLiteral = (schema: OpenApi.IJsonSchema): boolean =>
255
+ OpenApiTypeChecker.isReference(schema) ||
256
+ OpenApiTypeChecker.isBoolean(schema) ||
257
+ OpenApiTypeChecker.isNumber(schema) ||
258
+ OpenApiTypeChecker.isString(schema) ||
259
+ OpenApiTypeChecker.isUnknown(schema) ||
260
+ (OpenApiTypeChecker.isOneOf(schema) &&
351
261
  schema.oneOf.every(isNotObjectLiteral)) ||
352
- (SwaggerTypeChecker.isArray(schema) && isNotObjectLiteral(schema.items));
262
+ (OpenApiTypeChecker.isArray(schema) && isNotObjectLiteral(schema.items));
353
263
 
354
264
  const emplaceBodySchema =
355
265
  (from: "request" | "response") =>
356
- (emplacer: (schema: ISwaggerSchema) => ISwaggerSchema.IReference) =>
266
+ (
267
+ emplacer: (schema: OpenApi.IJsonSchema) => OpenApi.IJsonSchema.IReference,
268
+ ) =>
357
269
  (meta?: {
358
270
  description?: string;
359
- content?: ISwaggerRouteBodyContent;
271
+ content?: Record<string, OpenApi.IOperation.IMediaType>; // ISwaggerRouteBodyContent;
360
272
  "x-nestia-encrypted"?: boolean;
361
273
  }): false | null | IMigrateRoute.IBody => {
362
274
  if (!meta?.content) return null;
363
275
 
364
- const entries: [string, { schema: ISwaggerSchema }][] = Object.entries(
276
+ const entries: [string, OpenApi.IOperation.IMediaType][] = Object.entries(
365
277
  meta.content,
366
- );
278
+ ).filter(([_, v]) => !!v);
367
279
  const json = entries.find((e) =>
368
280
  meta["x-nestia-encrypted"] === true
369
281
  ? e[0].includes("text/plain") || e[0].includes("application/json")
@@ -375,7 +287,11 @@ export namespace MigrateMethodAnalzyer {
375
287
  type: "application/json",
376
288
  name: "body",
377
289
  key: "body",
378
- schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
290
+ schema: schema
291
+ ? isNotObjectLiteral(schema)
292
+ ? schema
293
+ : emplacer(schema)
294
+ : {},
379
295
  "x-nestia-encrypted": meta["x-nestia-encrypted"],
380
296
  };
381
297
  }
@@ -389,7 +305,11 @@ export namespace MigrateMethodAnalzyer {
389
305
  type: "application/x-www-form-urlencoded",
390
306
  name: "body",
391
307
  key: "body",
392
- schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
308
+ schema: schema
309
+ ? isNotObjectLiteral(schema)
310
+ ? schema
311
+ : emplacer(schema)
312
+ : {},
393
313
  };
394
314
  }
395
315
 
@@ -412,7 +332,11 @@ export namespace MigrateMethodAnalzyer {
412
332
  type: "multipart/form-data",
413
333
  name: "body",
414
334
  key: "body",
415
- schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
335
+ schema: schema
336
+ ? isNotObjectLiteral(schema)
337
+ ? schema
338
+ : emplacer(schema)
339
+ : {},
416
340
  };
417
341
  }
418
342
  }
@@ -420,9 +344,9 @@ export namespace MigrateMethodAnalzyer {
420
344
  };
421
345
 
422
346
  const emplaceReference =
423
- (swagger: ISwagger) =>
347
+ (swagger: OpenApi.IDocument) =>
424
348
  (name: string) =>
425
- (schema: ISwaggerSchema): ISwaggerSchema.IReference => {
349
+ (schema: OpenApi.IJsonSchema): OpenApi.IJsonSchema.IReference => {
426
350
  swagger.components.schemas ??= {};
427
351
  swagger.components.schemas[name] = schema;
428
352
  return { $ref: `#/components/schemas/${name}` };
@@ -47,7 +47,7 @@ export const NEST_TEMPLATE = [
47
47
  {
48
48
  "location": "",
49
49
  "file": "package.json",
50
- "content": "{\r\n \"private\": true,\r\n \"name\": \"@ORGANIZATION/PROJECT\",\r\n \"version\": \"0.1.0\",\r\n \"description\": \"Starter kit of Nestia\",\r\n \"main\": \"lib/index.js\",\r\n \"scripts\": {\r\n \"test\": \"node bin/test\",\r\n \"test:webpack\": \"npm run webpack && node bin/test/webpack.js\",\r\n \"------------------------BUILDS------------------------\": \"\",\r\n \"build\": \"npm run build:sdk && npm run build:main && npm run build:test\",\r\n \"build:api\": \"rimraf packages/api/lib && npm run build:sdk && tsc -p packages/api/tsconfig.json\",\r\n \"build:main\": \"rimraf lib && tsc\",\r\n \"build:sdk\": \"rimraf src/api/functional && nestia sdk\",\r\n \"build:swagger\": \"npx nestia swagger\",\r\n \"build:test\": \"rimraf bin && tsc -p test/tsconfig.json\",\r\n \"dev\": \"npm run build:test -- --watch\",\r\n \"eslint\": \"eslint src && eslint test\",\r\n \"eslint:fix\": \"eslint --fix src && eslint --fix test\",\r\n \"prepare\": \"ts-patch install && typia patch\",\r\n \"prettier\": \"prettier src --write && prettier test --write\",\r\n \"------------------------WEBPACK------------------------\": \"\",\r\n \"webpack\": \"rimraf dist && webpack\",\r\n \"webpack:start\": \"cd dist && node dist/server\",\r\n \"------------------------DEPLOYS------------------------\": \"\",\r\n \"package:api\": \"npm run build:swagger && npm run build:api && cd packages/api && npm publish\",\r\n \"start\": \"node lib/executable/server\",\r\n \"start:swagger\": \"ts-node src/executable/swagger.ts\"\r\n },\r\n \"repository\": {\r\n \"type\": \"git\",\r\n \"url\": \"https://github.com/samchon/nestia-start\"\r\n },\r\n \"keywords\": [\r\n \"nestia\",\r\n \"template\",\r\n \"boilerplate\"\r\n ],\r\n \"author\": \"AUTHOR\",\r\n \"license\": \"MIT\",\r\n \"bugs\": {\r\n \"url\": \"https://github.com/samchon/nestia-start/issues\"\r\n },\r\n \"homepage\": \"https://github.com/samchon/nestia-start#readme\",\r\n \"devDependencies\": {\r\n \"@nestia/e2e\": \"^0.4.2\",\r\n \"@nestia/sdk\": \"^2.6.4\",\r\n \"@trivago/prettier-plugin-sort-imports\": \"^4.3.0\",\r\n \"@types/cli\": \"^0.11.21\",\r\n \"@types/express\": \"^4.17.21\",\r\n \"@types/inquirer\": \"^8.2.5\",\r\n \"@types/node\": \"^18.11.0\",\r\n \"@types/uuid\": \"^8.3.4\",\r\n \"@typescript-eslint/eslint-plugin\": \"^6.19.1\",\r\n \"@typescript-eslint/parser\": \"^6.19.1\",\r\n \"chalk\": \"^4.1.0\",\r\n \"cli\": \"^1.0.1\",\r\n \"copy-webpack-plugin\": \"^11.0.0\",\r\n \"eslint-plugin-deprecation\": \"^2.0.0\",\r\n \"express\": \"^4.18.2\",\r\n \"nestia\": \"^5.3.0\",\r\n \"prettier\": \"^3.2.4\",\r\n \"prettier-plugin-prisma\": \"^5.0.0\",\r\n \"rimraf\": \"^3.0.2\",\r\n \"source-map-support\": \"^0.5.21\",\r\n \"swagger-ui-express\": \"^5.0.0\",\r\n \"ts-loader\": \"^9.5.1\",\r\n \"ts-node\": \"^10.9.1\",\r\n \"ts-patch\": \"^3.0.2\",\r\n \"typescript\": \"^5.3.2\",\r\n \"typescript-transform-paths\": \"^3.4.6\",\r\n \"webpack\": \"^5.89.0\",\r\n \"webpack-cli\": \"^5.1.4\",\r\n \"write-file-webpack-plugin\": \"^4.5.1\"\r\n },\r\n \"dependencies\": {\r\n \"@nestia/core\": \"^2.6.4\",\r\n \"@nestia/fetcher\": \"^2.6.4\",\r\n \"@nestjs/common\": \"^10.3.7\",\r\n \"@nestjs/core\": \"^10.3.7\",\r\n \"@nestjs/platform-express\": \"^10.3.7\",\r\n \"commander\": \"10.0.0\",\r\n \"dotenv\": \"^16.3.1\",\r\n \"dotenv-expand\": \"^10.0.0\",\r\n \"inquirer\": \"8.2.5\",\r\n \"serialize-error\": \"^4.1.0\",\r\n \"tstl\": \"^3.0.0\",\r\n \"typia\": \"^5.5.10\",\r\n \"uuid\": \"^9.0.0\"\r\n },\r\n \"stackblitz\": {\r\n \"startCommand\": \"npm run prepare && npm run build:test && npm run test\"\r\n }\r\n}"
50
+ "content": "{\r\n \"private\": true,\r\n \"name\": \"@ORGANIZATION/PROJECT\",\r\n \"version\": \"0.1.0\",\r\n \"description\": \"Starter kit of Nestia\",\r\n \"main\": \"lib/index.js\",\r\n \"scripts\": {\r\n \"test\": \"node bin/test\",\r\n \"test:webpack\": \"npm run webpack && node bin/test/webpack.js\",\r\n \"------------------------BUILDS------------------------\": \"\",\r\n \"build\": \"npm run build:sdk && npm run build:main && npm run build:test\",\r\n \"build:api\": \"rimraf packages/api/lib && npm run build:sdk && tsc -p packages/api/tsconfig.json\",\r\n \"build:main\": \"rimraf lib && tsc\",\r\n \"build:sdk\": \"rimraf src/api/functional && nestia sdk\",\r\n \"build:swagger\": \"npx nestia swagger\",\r\n \"build:test\": \"rimraf bin && tsc -p test/tsconfig.json\",\r\n \"dev\": \"npm run build:test -- --watch\",\r\n \"eslint\": \"eslint src && eslint test\",\r\n \"eslint:fix\": \"eslint --fix src && eslint --fix test\",\r\n \"prepare\": \"ts-patch install && typia patch\",\r\n \"prettier\": \"prettier src --write && prettier test --write\",\r\n \"------------------------WEBPACK------------------------\": \"\",\r\n \"webpack\": \"rimraf dist && webpack\",\r\n \"webpack:start\": \"cd dist && node dist/server\",\r\n \"------------------------DEPLOYS------------------------\": \"\",\r\n \"package:api\": \"npm run build:swagger && npm run build:api && cd packages/api && npm publish\",\r\n \"start\": \"node lib/executable/server\",\r\n \"start:swagger\": \"ts-node src/executable/swagger.ts\"\r\n },\r\n \"repository\": {\r\n \"type\": \"git\",\r\n \"url\": \"https://github.com/samchon/nestia-start\"\r\n },\r\n \"keywords\": [\r\n \"nestia\",\r\n \"template\",\r\n \"boilerplate\"\r\n ],\r\n \"author\": \"AUTHOR\",\r\n \"license\": \"MIT\",\r\n \"bugs\": {\r\n \"url\": \"https://github.com/samchon/nestia-start/issues\"\r\n },\r\n \"homepage\": \"https://github.com/samchon/nestia-start#readme\",\r\n \"devDependencies\": {\r\n \"@nestia/e2e\": \"^0.4.3\",\r\n \"@nestia/sdk\": \"^2.6.4\",\r\n \"@trivago/prettier-plugin-sort-imports\": \"^4.3.0\",\r\n \"@types/cli\": \"^0.11.21\",\r\n \"@types/express\": \"^4.17.21\",\r\n \"@types/inquirer\": \"^8.2.5\",\r\n \"@types/node\": \"^18.11.0\",\r\n \"@types/uuid\": \"^8.3.4\",\r\n \"@typescript-eslint/eslint-plugin\": \"^6.19.1\",\r\n \"@typescript-eslint/parser\": \"^6.19.1\",\r\n \"chalk\": \"^4.1.0\",\r\n \"cli\": \"^1.0.1\",\r\n \"copy-webpack-plugin\": \"^11.0.0\",\r\n \"eslint-plugin-deprecation\": \"^2.0.0\",\r\n \"express\": \"^4.18.2\",\r\n \"nestia\": \"^5.3.0\",\r\n \"prettier\": \"^3.2.4\",\r\n \"prettier-plugin-prisma\": \"^5.0.0\",\r\n \"rimraf\": \"^3.0.2\",\r\n \"source-map-support\": \"^0.5.21\",\r\n \"swagger-ui-express\": \"^5.0.0\",\r\n \"ts-loader\": \"^9.5.1\",\r\n \"ts-node\": \"^10.9.1\",\r\n \"ts-patch\": \"^3.0.2\",\r\n \"typescript\": \"^5.3.2\",\r\n \"typescript-transform-paths\": \"^3.4.6\",\r\n \"webpack\": \"^5.89.0\",\r\n \"webpack-cli\": \"^5.1.4\",\r\n \"write-file-webpack-plugin\": \"^4.5.1\"\r\n },\r\n \"dependencies\": {\r\n \"@nestia/core\": \"^2.6.4\",\r\n \"@nestia/fetcher\": \"^2.6.4\",\r\n \"@nestjs/common\": \"^10.3.7\",\r\n \"@nestjs/core\": \"^10.3.7\",\r\n \"@nestjs/platform-express\": \"^10.3.7\",\r\n \"commander\": \"10.0.0\",\r\n \"dotenv\": \"^16.3.1\",\r\n \"dotenv-expand\": \"^10.0.0\",\r\n \"inquirer\": \"8.2.5\",\r\n \"serialize-error\": \"^4.1.0\",\r\n \"tstl\": \"^3.0.0\",\r\n \"typia\": \"^5.5.10\",\r\n \"uuid\": \"^9.0.0\"\r\n },\r\n \"stackblitz\": {\r\n \"startCommand\": \"npm run prepare && npm run build:test && npm run test\"\r\n }\r\n}"
51
51
  },
52
52
  {
53
53
  "location": "packages/api",
@@ -21,7 +21,14 @@ export namespace TypeLiteralFactory {
21
21
  ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(str));
22
22
 
23
23
  const generateNumber = (num: number) =>
24
- ts.factory.createLiteralTypeNode(ts.factory.createNumericLiteral(num));
24
+ ts.factory.createLiteralTypeNode(
25
+ num < 0
26
+ ? ts.factory.createPrefixUnaryExpression(
27
+ ts.SyntaxKind.MinusToken,
28
+ ts.factory.createNumericLiteral(-num),
29
+ )
30
+ : ts.factory.createNumericLiteral(num),
31
+ );
25
32
 
26
33
  const generateBoolean = (bool: boolean) =>
27
34
  ts.factory.createLiteralTypeNode(
@@ -1,3 +1,4 @@
1
+ import { OpenApi } from "@samchon/openapi";
1
2
  import fs from "fs";
2
3
  import path from "path";
3
4
  import { format } from "prettier";
@@ -5,7 +6,6 @@ import { IValidation } from "typia";
5
6
 
6
7
  import { MigrateApplication } from "../MigrateApplication";
7
8
  import { MigrateFileArchiver } from "../archivers/MigrateFileArchiver";
8
- import { ISwagger } from "../structures/ISwagger";
9
9
  import { MigrateInquirer } from "./MigrateInquirer";
10
10
 
11
11
  export namespace MigrateCommander {
@@ -23,19 +23,19 @@ export namespace MigrateCommander {
23
23
  halt("Output directory's parent is not a directory.");
24
24
 
25
25
  // READ SWAGGER
26
- const swagger: ISwagger = (() => {
26
+ const document: OpenApi.IDocument = (() => {
27
27
  if (fs.existsSync(options.input) === false)
28
28
  halt("Unable to find the input swagger.json file.");
29
29
  const stats: fs.Stats = fs.statSync(options.input);
30
30
  if (stats.isFile() === false)
31
31
  halt("The input swagger.json is not a file.");
32
32
  const content: string = fs.readFileSync(options.input, "utf-8");
33
- const swagger: ISwagger = JSON.parse(content);
33
+ const swagger: OpenApi.IDocument = JSON.parse(content);
34
34
  return swagger;
35
35
  })();
36
36
 
37
37
  const result: IValidation<MigrateApplication> =
38
- await MigrateApplication.create(swagger);
38
+ await MigrateApplication.create(document);
39
39
  if (result.success === false) {
40
40
  console.log(result.errors);
41
41
  throw new Error(
package/src/module.ts CHANGED
@@ -4,11 +4,5 @@ export * from "./analyzers/MigrateAnalyzer";
4
4
 
5
5
  export * from "./archivers/MigrateFileArchiver";
6
6
 
7
- export * from "./structures/ISwagger";
8
- export * from "./structures/ISwaggerComponents";
9
- export * from "./structures/ISwaggerInfo";
10
- export * from "./structures/ISwaggerRoute";
11
- export * from "./structures/ISwaggerSecurityScheme";
12
- export * from "./structures/ISwaggerSchema";
13
7
  export * from "./structures/IMigrateProgram";
14
8
  export * from "./structures/IMigrateSchema";
@@ -1,9 +1,9 @@
1
+ import { OpenApi } from "@samchon/openapi";
1
2
  import ts from "typescript";
2
3
 
3
4
  import { IMigrateController } from "../structures/IMigrateController";
4
5
  import { IMigrateProgram } from "../structures/IMigrateProgram";
5
6
  import { IMigrateRoute } from "../structures/IMigrateRoute";
6
- import { ISwaggerComponents } from "../structures/ISwaggerComponents";
7
7
  import { MigrateApiFunctionProgrammer } from "./MigrateApiFunctionProgrammer";
8
8
  import { MigrateApiNamespaceProgrammer } from "./MigrateApiNamespaceProgrammer";
9
9
  import { MigrateImportProgrammer } from "./MigrateImportProgrammer";
@@ -22,7 +22,7 @@ export namespace MigrateApiFileProgrammer {
22
22
 
23
23
  export const write =
24
24
  (config: IMigrateProgram.IConfig) =>
25
- (components: ISwaggerComponents) =>
25
+ (components: OpenApi.IComponents) =>
26
26
  (props: IProps): ts.Statement[] => {
27
27
  const importer: MigrateImportProgrammer = new MigrateImportProgrammer();
28
28
  const statements: ts.Statement[] = props.entries
@@ -1,10 +1,10 @@
1
+ import { OpenApi } from "@samchon/openapi";
1
2
  import ts from "typescript";
2
3
  import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory";
3
4
 
4
5
  import { IMigrateController } from "../structures/IMigrateController";
5
6
  import { IMigrateProgram } from "../structures/IMigrateProgram";
6
7
  import { IMigrateRoute } from "../structures/IMigrateRoute";
7
- import { ISwaggerComponents } from "../structures/ISwaggerComponents";
8
8
  import { FilePrinter } from "../utils/FilePrinter";
9
9
  import { MigrateImportProgrammer } from "./MigrateImportProgrammer";
10
10
  import { MigrateSchemaProgrammer } from "./MigrateSchemaProgrammer";
@@ -18,7 +18,7 @@ export namespace MigrateApiFunctionProgrammer {
18
18
 
19
19
  export const write =
20
20
  (config: IMigrateProgram.IConfig) =>
21
- (components: ISwaggerComponents) =>
21
+ (components: OpenApi.IComponents) =>
22
22
  (importer: MigrateImportProgrammer) =>
23
23
  (props: IProps): ts.FunctionDeclaration =>
24
24
  FilePrinter.description(
@@ -42,7 +42,7 @@ export namespace MigrateApiFunctionProgrammer {
42
42
  );
43
43
 
44
44
  export const writeParameterDeclarations =
45
- (components: ISwaggerComponents) =>
45
+ (components: OpenApi.IComponents) =>
46
46
  (importer: MigrateImportProgrammer) =>
47
47
  (props: IProps): ts.ParameterDeclaration[] => [
48
48
  IdentifierFactory.parameter(
@@ -1,3 +1,4 @@
1
+ import { OpenApi } from "@samchon/openapi";
1
2
  import ts from "typescript";
2
3
  import { ExpressionFactory } from "typia/lib/factories/ExpressionFactory";
3
4
  import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory";
@@ -7,7 +8,6 @@ import { TypeFactory } from "typia/lib/factories/TypeFactory";
7
8
  import { IMigrateController } from "../structures/IMigrateController";
8
9
  import { IMigrateProgram } from "../structures/IMigrateProgram";
9
10
  import { IMigrateRoute } from "../structures/IMigrateRoute";
10
- import { ISwaggerComponents } from "../structures/ISwaggerComponents";
11
11
  import { FilePrinter } from "../utils/FilePrinter";
12
12
  import { MigrateApiSimulatationProgrammer } from "./MigrateApiSimulatationProgrammer";
13
13
  import { MigrateImportProgrammer } from "./MigrateImportProgrammer";
@@ -22,7 +22,7 @@ export namespace MigrateApiNamespaceProgrammer {
22
22
 
23
23
  export const write =
24
24
  (config: IMigrateProgram.IConfig) =>
25
- (components: ISwaggerComponents) =>
25
+ (components: OpenApi.IComponents) =>
26
26
  (importer: MigrateImportProgrammer) =>
27
27
  (props: IProps): ts.ModuleDeclaration => {
28
28
  const types = writeTypes(components)(importer)(props.route);
@@ -65,7 +65,7 @@ export namespace MigrateApiNamespaceProgrammer {
65
65
  );
66
66
 
67
67
  const writeTypes =
68
- (components: ISwaggerComponents) =>
68
+ (components: OpenApi.IComponents) =>
69
69
  (importer: MigrateImportProgrammer) =>
70
70
  (route: IMigrateRoute): ts.TypeAliasDeclaration[] => {
71
71
  const array: ts.TypeAliasDeclaration[] = [];
@@ -110,7 +110,7 @@ export namespace MigrateApiNamespaceProgrammer {
110
110
  };
111
111
 
112
112
  const writeMetadata =
113
- (components: ISwaggerComponents) =>
113
+ (components: OpenApi.IComponents) =>
114
114
  (importer: MigrateImportProgrammer) =>
115
115
  (props: IProps): ts.VariableStatement =>
116
116
  constant("METADATA")(
@@ -178,7 +178,7 @@ export namespace MigrateApiNamespaceProgrammer {
178
178
  );
179
179
 
180
180
  const writePath =
181
- (components: ISwaggerComponents) =>
181
+ (components: OpenApi.IComponents) =>
182
182
  (importer: MigrateImportProgrammer) =>
183
183
  (props: IProps): ts.VariableStatement => {
184
184
  const out = (body: ts.ConciseBody) =>
@@ -58,14 +58,16 @@ export namespace MigrateApiProgrammer {
58
58
  file: "index.ts",
59
59
  content: FilePrinter.write({
60
60
  statements: MigrateApiFileProgrammer.write(program)(
61
- program.swagger.components,
61
+ program.document.components,
62
62
  )(props),
63
63
  }),
64
64
  }));
65
65
  if (program.mode === "sdk")
66
66
  output.push(
67
67
  ...[
68
- ...MigrateDtoProgrammer.compose(program.swagger.components).entries(),
68
+ ...MigrateDtoProgrammer.compose(
69
+ program.document.components,
70
+ ).entries(),
69
71
  ].map(([key, value]) => ({
70
72
  location: "src/structures",
71
73
  file: `${key}.ts`,