@nestia/migrate 0.5.1 → 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.
Files changed (103) hide show
  1. package/lib/{NestiaMigrateApplication.d.ts → MigrateApplication.d.ts} +1 -1
  2. package/lib/{NestiaMigrateApplication.js → MigrateApplication.js} +38 -32
  3. package/lib/MigrateApplication.js.map +1 -0
  4. package/lib/analyzers/ControllerAnalyzer.d.ts +5 -0
  5. package/lib/{programmers/ControllerProgrammer.js → analyzers/ControllerAnalyzer.js} +8 -32
  6. package/lib/analyzers/ControllerAnalyzer.js.map +1 -0
  7. package/lib/analyzers/MethodAnalyzer.d.ts +9 -0
  8. package/lib/{programmers/RouteProgrammer.js → analyzers/MethodAnalyzer.js} +30 -134
  9. package/lib/analyzers/MethodAnalyzer.js.map +1 -0
  10. package/lib/analyzers/MigrateAnalyzer.d.ts +4 -0
  11. package/lib/analyzers/MigrateAnalyzer.js +12 -0
  12. package/lib/analyzers/MigrateAnalyzer.js.map +1 -0
  13. package/lib/analyzers/RouteAnalyzer.d.ts +0 -0
  14. package/lib/analyzers/RouteAnalyzer.js +2 -0
  15. package/lib/analyzers/RouteAnalyzer.js.map +1 -0
  16. package/lib/archivers/FileArchiver.js.map +1 -1
  17. package/lib/bundles/TEMPLATE.js +10 -10
  18. package/lib/bundles/TEMPLATE.js.map +1 -1
  19. package/lib/executable/bundle.js.map +1 -1
  20. package/lib/executable/migrate.js +7 -7
  21. package/lib/executable/migrate.js.map +1 -1
  22. package/lib/module.d.ts +1 -1
  23. package/lib/module.js +1 -1
  24. package/lib/module.js.map +1 -1
  25. package/lib/programmers/DtoProgrammer.d.ts +8 -4
  26. package/lib/programmers/DtoProgrammer.js +36 -77
  27. package/lib/programmers/DtoProgrammer.js.map +1 -1
  28. package/lib/programmers/ImportProgrammer.d.ts +5 -5
  29. package/lib/programmers/ImportProgrammer.js +27 -19
  30. package/lib/programmers/ImportProgrammer.js.map +1 -1
  31. package/lib/programmers/{ControllerProgrammer.d.ts → NestControllerProgrammer.d.ts} +3 -4
  32. package/lib/programmers/NestControllerProgrammer.js +30 -0
  33. package/lib/programmers/NestControllerProgrammer.js.map +1 -0
  34. package/lib/programmers/NestMethodProgrammer.d.ts +7 -0
  35. package/lib/programmers/NestMethodProgrammer.js +103 -0
  36. package/lib/programmers/NestMethodProgrammer.js.map +1 -0
  37. package/lib/programmers/NestModuleProgrammer.d.ts +5 -0
  38. package/lib/programmers/NestModuleProgrammer.js +29 -0
  39. package/lib/programmers/NestModuleProgrammer.js.map +1 -0
  40. package/lib/programmers/NestProgrammer.d.ts +5 -0
  41. package/lib/programmers/NestProgrammer.js +60 -0
  42. package/lib/programmers/NestProgrammer.js.map +1 -0
  43. package/lib/programmers/SchemaProgrammer.d.ts +2 -1
  44. package/lib/programmers/SchemaProgrammer.js +122 -189
  45. package/lib/programmers/SchemaProgrammer.js.map +1 -1
  46. package/lib/structures/IMigrateProgram.d.ts +2 -2
  47. package/lib/structures/ISwaggerInfo.d.ts +3 -3
  48. package/lib/utils/FilePrinter.d.ts +9 -0
  49. package/lib/utils/FilePrinter.js +25 -0
  50. package/lib/utils/FilePrinter.js.map +1 -0
  51. package/lib/utils/JsonTypeChecker.d.ts +3 -1
  52. package/lib/utils/JsonTypeChecker.js +31 -18
  53. package/lib/utils/JsonTypeChecker.js.map +1 -1
  54. package/lib/utils/MapUtil.js.map +1 -1
  55. package/lib/utils/SetupWizard.js.map +1 -1
  56. package/lib/utils/StringUtil.js.map +1 -1
  57. package/package.json +8 -6
  58. package/src/MigrateApplication.ts +73 -0
  59. package/src/analyzers/ControllerAnalyzer.ts +107 -0
  60. package/src/analyzers/MethodAnalyzer.ts +315 -0
  61. package/src/analyzers/MigrateAnalyzer.ts +9 -0
  62. package/src/analyzers/RouteAnalyzer.ts +0 -0
  63. package/src/archivers/FileArchiver.ts +35 -38
  64. package/src/bundles/TEMPLATE.ts +10 -10
  65. package/src/executable/bundle.ts +72 -78
  66. package/src/executable/migrate.ts +59 -60
  67. package/src/index.ts +4 -4
  68. package/src/module.ts +4 -4
  69. package/src/programmers/DtoProgrammer.ts +74 -118
  70. package/src/programmers/ImportProgrammer.ts +98 -60
  71. package/src/programmers/NestControllerProgrammer.ts +47 -0
  72. package/src/programmers/NestMethodProgrammer.ts +211 -0
  73. package/src/programmers/NestModuleProgrammer.ts +62 -0
  74. package/src/programmers/NestProgrammer.ts +74 -0
  75. package/src/programmers/SchemaProgrammer.ts +247 -339
  76. package/src/structures/IMigrateController.ts +8 -8
  77. package/src/structures/IMigrateDto.ts +8 -8
  78. package/src/structures/IMigrateFile.ts +5 -5
  79. package/src/structures/IMigrateProgram.ts +7 -7
  80. package/src/structures/IMigrateRoute.ts +36 -36
  81. package/src/structures/IMigrateSchema.ts +4 -4
  82. package/src/structures/ISwaggeSchema.ts +82 -82
  83. package/src/structures/ISwagger.ts +20 -20
  84. package/src/structures/ISwaggerComponents.ts +7 -7
  85. package/src/structures/ISwaggerInfo.ts +57 -57
  86. package/src/structures/ISwaggerRoute.ts +52 -52
  87. package/src/structures/ISwaggerSecurity.ts +47 -47
  88. package/src/utils/FilePrinter.ts +36 -0
  89. package/src/utils/JsonTypeChecker.ts +67 -52
  90. package/src/utils/MapUtil.ts +13 -13
  91. package/src/utils/SetupWizard.ts +15 -15
  92. package/src/utils/StringUtil.ts +51 -51
  93. package/lib/NestiaMigrateApplication.js.map +0 -1
  94. package/lib/programmers/ControllerProgrammer.js.map +0 -1
  95. package/lib/programmers/MigrateProgrammer.d.ts +0 -8
  96. package/lib/programmers/MigrateProgrammer.js +0 -48
  97. package/lib/programmers/MigrateProgrammer.js.map +0 -1
  98. package/lib/programmers/RouteProgrammer.d.ts +0 -13
  99. package/lib/programmers/RouteProgrammer.js.map +0 -1
  100. package/src/NestiaMigrateApplication.ts +0 -73
  101. package/src/programmers/ControllerProgrammer.ts +0 -157
  102. package/src/programmers/MigrateProgrammer.ts +0 -62
  103. package/src/programmers/RouteProgrammer.ts +0 -506
@@ -0,0 +1,315 @@
1
+ import { Escaper } from "typia/lib/utils/Escaper";
2
+
3
+ import { ISwagger } from "../module";
4
+ import { IMigrateRoute } from "../structures/IMigrateRoute";
5
+ import { ISwaggerSchema } from "../structures/ISwaggeSchema";
6
+ import { ISwaggerRoute } from "../structures/ISwaggerRoute";
7
+ import { SwaggerTypeChecker } from "../utils/JsonTypeChecker";
8
+ import { StringUtil } from "../utils/StringUtil";
9
+
10
+ export namespace MethodAnalzyer {
11
+ export const analyze =
12
+ (swagger: ISwagger) =>
13
+ (props: { path: string; method: string }) =>
14
+ (route: ISwaggerRoute): IMigrateRoute | null => {
15
+ const body = emplaceBodySchema(emplaceReference(swagger)("body"))(
16
+ route.requestBody,
17
+ );
18
+ const success = emplaceBodySchema(emplaceReference(swagger)("response"))(
19
+ route.responses?.["201"] ?? route.responses?.["200"],
20
+ );
21
+ if (body === false || success === false) {
22
+ console.log(
23
+ `Failed to migrate ${props.method.toUpperCase()} ${
24
+ props.path
25
+ }: @nestia/migrate supports only application/json, application/x-www-form-urlencoded or text/plain format yet.`,
26
+ );
27
+ return null;
28
+ } else if (SUPPORTED_METHODS.has(props.method.toUpperCase()) === false) {
29
+ console.log(
30
+ `Failed to migrate ${props.method.toUpperCase()} ${
31
+ props.path
32
+ }: @nestia/migrate does not support ${props.method.toUpperCase()} method.`,
33
+ );
34
+ return null;
35
+ }
36
+
37
+ const [headers, query] = ["header", "query"].map((type) => {
38
+ const parameters = (route.parameters ?? []).filter(
39
+ (p) => p.in === type,
40
+ );
41
+ if (parameters.length === 0) return null;
42
+
43
+ const objects = parameters
44
+ .map((p) =>
45
+ SwaggerTypeChecker.isObject(p.schema)
46
+ ? p.schema
47
+ : SwaggerTypeChecker.isReference(p.schema) &&
48
+ SwaggerTypeChecker.isObject(
49
+ (swagger.components.schemas ?? {})[
50
+ p.schema.$ref.replace(`#/components/schemas/`, ``)
51
+ ] ?? {},
52
+ )
53
+ ? p.schema
54
+ : null!,
55
+ )
56
+ .filter((s) => !!s);
57
+ const primitives = parameters.filter(
58
+ (p) =>
59
+ SwaggerTypeChecker.isBoolean(p.schema) ||
60
+ SwaggerTypeChecker.isNumber(p.schema) ||
61
+ SwaggerTypeChecker.isInteger(p.schema) ||
62
+ SwaggerTypeChecker.isString(p.schema) ||
63
+ SwaggerTypeChecker.isArray(p.schema),
64
+ );
65
+ if (objects.length === 1 && primitives.length === 0) return objects[0];
66
+ else if (objects.length > 1)
67
+ throw new Error(
68
+ `Error on nestia.migrate.RouteProgrammer.analze(): ${type} typed parameters must be only one object type - ${StringUtil.capitalize(
69
+ props.method,
70
+ )} "${props.path}".`,
71
+ );
72
+
73
+ const dto: ISwaggerSchema.IObject | null = objects[0]
74
+ ? SwaggerTypeChecker.isObject(objects[0])
75
+ ? objects[0]
76
+ : ((swagger.components.schemas ?? {})[
77
+ (objects[0] as ISwaggerSchema.IReference).$ref.replace(
78
+ `#/components/schemas/`,
79
+ ``,
80
+ )
81
+ ] as ISwaggerSchema.IObject)
82
+ : null;
83
+ const entire: ISwaggerSchema.IObject[] = [
84
+ ...objects.map((o) =>
85
+ SwaggerTypeChecker.isObject(o)
86
+ ? o
87
+ : (swagger.components.schemas?.[
88
+ o.$ref.replace(`#/components/schemas/`, ``)
89
+ ]! as ISwaggerSchema.IObject),
90
+ ),
91
+ {
92
+ type: "object",
93
+ properties: Object.fromEntries([
94
+ ...primitives.map((p) => [
95
+ p.name,
96
+ {
97
+ ...p.schema,
98
+ description: p.schema.description ?? p.description,
99
+ },
100
+ ]),
101
+ ...(dto ? Object.entries(dto.properties ?? {}) : []),
102
+ ]),
103
+ required: [
104
+ ...primitives.filter((p) => p.required).map((p) => p.name!),
105
+ ...(dto ? dto.required ?? [] : []),
106
+ ],
107
+ },
108
+ ];
109
+ return parameters.length === 0
110
+ ? null
111
+ : emplaceReference(swagger)(
112
+ StringUtil.pascal(`I/Api/${props.path}`) +
113
+ "." +
114
+ StringUtil.pascal(`${props.method}/${type}`),
115
+ )({
116
+ type: "object",
117
+ properties: Object.fromEntries([
118
+ ...new Map<string, ISwaggerSchema>(
119
+ entire
120
+ .map((o) =>
121
+ Object.entries(o.properties ?? {}).map(
122
+ ([name, schema]) =>
123
+ [
124
+ name,
125
+ {
126
+ ...schema,
127
+ description:
128
+ schema.description ?? schema.description,
129
+ } as ISwaggerSchema,
130
+ ] as const,
131
+ ),
132
+ )
133
+ .flat(),
134
+ ),
135
+ ]),
136
+ required: [
137
+ ...new Set(entire.map((o) => o.required ?? []).flat()),
138
+ ],
139
+ });
140
+ });
141
+
142
+ const parameterNames: string[] = StringUtil.splitWithNormalization(
143
+ props.path,
144
+ )
145
+ .filter((str) => str[0] === "{" || str[0] === ":")
146
+ .map((str) =>
147
+ str[0] === "{" ? str.substring(1, str.length - 1) : str.substring(1),
148
+ );
149
+ if (
150
+ parameterNames.length !==
151
+ (route.parameters ?? []).filter((p) => p.in === "path").length
152
+ ) {
153
+ console.log(
154
+ `Failed to migrate ${props.method.toUpperCase()} ${
155
+ props.path
156
+ }: number of path parameters are not matched with its full path.`,
157
+ );
158
+ return null;
159
+ }
160
+ return {
161
+ name: "@lazy",
162
+ path: props.path,
163
+ method: props.method,
164
+ headers,
165
+ parameters: (route.parameters ?? [])
166
+ .filter((p) => p.in === "path")
167
+ .map((p, i) => ({
168
+ key: (() => {
169
+ let key: string = StringUtil.normalize(parameterNames[i]);
170
+ if (Escaper.variable(key)) return key;
171
+
172
+ while (true) {
173
+ key = "_" + key;
174
+ if (!parameterNames.some((s) => s === key)) return key;
175
+ }
176
+ })(),
177
+ schema: {
178
+ ...p.schema,
179
+ description: p.schema.description ?? p.description,
180
+ },
181
+ })),
182
+ query,
183
+ body,
184
+ success,
185
+ exceptions: Object.fromEntries(
186
+ Object.entries(route.responses ?? {})
187
+ .filter(
188
+ ([key, value]) =>
189
+ key !== "200" &&
190
+ key !== "201" &&
191
+ !!value.content?.["application/json"],
192
+ )
193
+ .map(([key, value]) => [
194
+ key,
195
+ {
196
+ description: value.description,
197
+ schema: value.content?.["application/json"]?.schema ?? {},
198
+ },
199
+ ]),
200
+ ),
201
+ description: describe(route),
202
+ "x-nestia-jsDocTags": route["x-nestia-jsDocTags"],
203
+ };
204
+ };
205
+
206
+ const describe = (route: ISwaggerRoute): string | undefined => {
207
+ const commentTags: string[] = [];
208
+ const add = (text: string) => {
209
+ if (commentTags.every((line) => line !== text)) commentTags.push(text);
210
+ };
211
+
212
+ let description: string | undefined = route.description;
213
+ if (route.summary) {
214
+ const emended: string = route.summary.endsWith(".")
215
+ ? route.summary
216
+ : route.summary + ".";
217
+ if (
218
+ description !== undefined &&
219
+ !description?.startsWith(route.summary) &&
220
+ !route["x-nestia-jsDocTags"]?.some((t) => t.name === "summary")
221
+ )
222
+ description = `${emended}\n${description}`;
223
+ }
224
+ if (route.tags) route.tags.forEach((name) => add(`@tag ${name}`));
225
+ if (route.deprecated) add("@deprecated");
226
+ for (const security of route.security ?? [])
227
+ for (const [name, scopes] of Object.entries(security))
228
+ add(`@security ${[name, ...scopes].join("")}`);
229
+ for (const jsDocTag of route["x-nestia-jsDocTags"] ?? [])
230
+ if (jsDocTag.text?.length)
231
+ add(
232
+ `@${jsDocTag.name} ${jsDocTag.text
233
+ .map((text) => text.text)
234
+ .join("")}`,
235
+ );
236
+ else add(`@${jsDocTag.name}`);
237
+ return description?.length
238
+ ? commentTags.length
239
+ ? `${description}\n\n${commentTags.join("\n")}`
240
+ : description
241
+ : commentTags.join("\n");
242
+ };
243
+
244
+ const isNotObjectLiteral = (schema: ISwaggerSchema): boolean =>
245
+ SwaggerTypeChecker.isReference(schema) ||
246
+ SwaggerTypeChecker.isBoolean(schema) ||
247
+ SwaggerTypeChecker.isNumber(schema) ||
248
+ SwaggerTypeChecker.isString(schema) ||
249
+ SwaggerTypeChecker.isUnknown(schema) ||
250
+ (SwaggerTypeChecker.isAnyOf(schema) &&
251
+ schema.anyOf.every(isNotObjectLiteral)) ||
252
+ (SwaggerTypeChecker.isOneOf(schema) &&
253
+ schema.oneOf.every(isNotObjectLiteral)) ||
254
+ (SwaggerTypeChecker.isArray(schema) && isNotObjectLiteral(schema.items));
255
+
256
+ const emplaceBodySchema =
257
+ (emplacer: (schema: ISwaggerSchema) => ISwaggerSchema.IReference) =>
258
+ (meta?: {
259
+ description?: string;
260
+ content?: ISwaggerRoute.IContent;
261
+ "x-nestia-encrypted"?: boolean;
262
+ }): false | null | IMigrateRoute.IBody => {
263
+ if (!meta?.content) return null;
264
+
265
+ const entries: [string, { schema: ISwaggerSchema }][] = Object.entries(
266
+ meta.content,
267
+ );
268
+ const json = entries.find((e) =>
269
+ meta["x-nestia-encrypted"] === true
270
+ ? e[0].includes("text/plain") || e[0].includes("application/json")
271
+ : e[0].includes("application/json") || e[0].includes("*/*"),
272
+ );
273
+ if (json) {
274
+ const { schema } = json[1];
275
+ return {
276
+ type: "application/json",
277
+ schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
278
+ "x-nestia-encrypted": meta["x-nestia-encrypted"],
279
+ };
280
+ }
281
+
282
+ const query = entries.find((e) =>
283
+ e[0].includes("application/x-www-form-urlencoded"),
284
+ );
285
+ if (query) {
286
+ const { schema } = query[1];
287
+ return {
288
+ type: "application/x-www-form-urlencoded",
289
+ schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
290
+ };
291
+ }
292
+
293
+ const text = entries.find((e) => e[0].includes("text/plain"));
294
+ if (text) return { type: "text/plain", schema: { type: "string" } };
295
+ return false;
296
+ };
297
+
298
+ const emplaceReference =
299
+ (swagger: ISwagger) =>
300
+ (name: string) =>
301
+ (schema: ISwaggerSchema): ISwaggerSchema.IReference => {
302
+ swagger.components.schemas ??= {};
303
+ swagger.components.schemas[name] = schema;
304
+ return { $ref: `#/components/schemas/${name}` };
305
+ };
306
+ }
307
+
308
+ const SUPPORTED_METHODS: Set<string> = new Set([
309
+ "GET",
310
+ "POST",
311
+ "PUT",
312
+ "PATCH",
313
+ "DELETE",
314
+ "HEAD",
315
+ ]);
@@ -0,0 +1,9 @@
1
+ import { IMigrateProgram, ISwagger } from "../module";
2
+ import { ControllerAnalyzer } from "./ControllerAnalyzer";
3
+
4
+ export namespace MigrateAnalyzer {
5
+ export const analyze = (swagger: ISwagger): IMigrateProgram => ({
6
+ swagger,
7
+ controllers: ControllerAnalyzer.analyze(swagger),
8
+ });
9
+ }
File without changes
@@ -1,38 +1,35 @@
1
- import { IMigrateFile } from "../structures/IMigrateFile";
2
-
3
- export namespace FileArchiver {
4
- export interface IOperator {
5
- mkdir(path: string): void;
6
- writeFile(path: string, content: string): void;
7
- }
8
-
9
- export const archive =
10
- (operator: IOperator) =>
11
- (output: string) =>
12
- (files: IMigrateFile[]): void => {
13
- const visited: Set<string> = new Set();
14
- for (const f of files) {
15
- mkdir(operator.mkdir)(output)(visited)(f.location);
16
- operator.writeFile(
17
- [output, f.location, f.file].join("/"),
18
- f.content,
19
- );
20
- }
21
- };
22
-
23
- const mkdir =
24
- (creator: (path: string) => void) =>
25
- (output: string) =>
26
- (visited: Set<string>) =>
27
- (path: string): void => {
28
- const sequence: string[] = path
29
- .split("/")
30
- .map((_str, i, entire) => entire.slice(0, i + 1).join("/"));
31
- for (const s of sequence)
32
- if (visited.has(s) === false)
33
- try {
34
- creator([output, s].join("/"));
35
- visited.add(s);
36
- } catch {}
37
- };
38
- }
1
+ import { IMigrateFile } from "../structures/IMigrateFile";
2
+
3
+ export namespace FileArchiver {
4
+ export interface IOperator {
5
+ mkdir(path: string): void;
6
+ writeFile(path: string, content: string): void;
7
+ }
8
+
9
+ export const archive =
10
+ (operator: IOperator) =>
11
+ (output: string) =>
12
+ (files: IMigrateFile[]): void => {
13
+ const visited: Set<string> = new Set();
14
+ for (const f of files) {
15
+ mkdir(operator.mkdir)(output)(visited)(f.location);
16
+ operator.writeFile([output, f.location, f.file].join("/"), f.content);
17
+ }
18
+ };
19
+
20
+ const mkdir =
21
+ (creator: (path: string) => void) =>
22
+ (output: string) =>
23
+ (visited: Set<string>) =>
24
+ (path: string): void => {
25
+ const sequence: string[] = path
26
+ .split("/")
27
+ .map((_str, i, entire) => entire.slice(0, i + 1).join("/"));
28
+ for (const s of sequence)
29
+ if (visited.has(s) === false)
30
+ try {
31
+ creator([output, s].join("/"));
32
+ visited.add(s);
33
+ } catch {}
34
+ };
35
+ }
@@ -7,12 +7,12 @@ export const TEMPLATE = [
7
7
  {
8
8
  "location": "",
9
9
  "file": ".eslintrc.cjs",
10
- "content": "module.exports = {\r\n root: true,\r\n plugins: [\r\n \"@typescript-eslint\",\r\n \"deprecation\",\r\n ],\r\n extends: [\r\n \"plugin:@typescript-eslint/recommended\",\r\n ],\r\n parser: \"@typescript-eslint/parser\",\r\n parserOptions: {\r\n project: [\r\n \"tsconfig.json\",\r\n \"test/tsconfig.json\",\r\n ]\r\n },\r\n overrides: [\r\n {\r\n files: [\"src/**/*.ts\", \"test/**/*.ts\"],\r\n rules: {\r\n \"@typescript-eslint/no-duplicate-imports\": \"error\",\r\n \"@typescript-eslint/no-explicit-any\": \"off\",\r\n \"@typescript-eslint/no-floating-promises\": \"error\",\r\n \"@typescript-eslint/no-inferrable-types\": \"off\",\r\n \"@typescript-eslint/no-namespace\": \"off\",\r\n \"@typescript-eslint/no-non-null-assertion\": \"off\",\r\n \"@typescript-eslint/no-var-requires\": \"off\",\r\n \"deprecation/deprecation\": \"error\",\r\n }\r\n }\r\n ]\r\n};"
10
+ "content": "module.exports = {\r\n root: true,\r\n plugins: [\"@typescript-eslint\", \"deprecation\"],\r\n extends: [\"plugin:@typescript-eslint/recommended\"],\r\n parser: \"@typescript-eslint/parser\",\r\n parserOptions: {\r\n project: [\"tsconfig.json\", \"test/tsconfig.json\"],\r\n },\r\n overrides: [\r\n {\r\n files: [\"src/**/*.ts\", \"test/**/*.ts\"],\r\n rules: {\r\n \"@typescript-eslint/no-explicit-any\": \"off\",\r\n \"@typescript-eslint/no-floating-promises\": \"error\",\r\n \"@typescript-eslint/no-inferrable-types\": \"off\",\r\n \"@typescript-eslint/no-namespace\": \"off\",\r\n \"@typescript-eslint/no-non-null-assertion\": \"off\",\r\n \"@typescript-eslint/no-var-requires\": \"off\",\r\n \"deprecation/deprecation\": \"error\",\r\n },\r\n },\r\n ],\r\n};\r\n"
11
11
  },
12
12
  {
13
13
  "location": "/.github",
14
14
  "file": "dependabot.yml",
15
- "content": "version: 2\r\nupdates:\r\n#######################################################\r\n - package-ecosystem: 'npm'\r\n directory: '/'\r\n schedule:\r\n interval: 'daily'\r\n open-pull-requests-limit: 25\r\n versioning-strategy: increase\r\n allow:\r\n - dependency-name: \"@nestia/*\"\r\n - dependency-name: \"nestia\"\r\n - dependency-name: \"tstl\"\r\n - dependency-name: \"typia\"\r\n#######################################################\r\n - package-ecosystem: 'npm'\r\n directory: '/packages/api'\r\n schedule:\r\n interval: 'daily'\r\n open-pull-requests-limit: 25\r\n versioning-strategy: increase\r\n#######################################################"
15
+ "content": "version: 2\r\nupdates:\r\n#######################################################\r\n - package-ecosystem: 'npm'\r\n directory: '/'\r\n schedule:\r\n interval: 'daily'\r\n open-pull-requests-limit: 25\r\n allow:\r\n - dependency-name: \"@nestjs/*\"\r\n - dependency-name: \"@nestia/*\"\r\n - dependency-name: \"nestia\"\r\n - dependency-name: \"tstl\"\r\n - dependency-name: \"typia\"\r\n#######################################################\r\n - package-ecosystem: 'npm'\r\n directory: '/packages/api'\r\n schedule:\r\n interval: 'daily'\r\n open-pull-requests-limit: 25\r\n#######################################################"
16
16
  },
17
17
  {
18
18
  "location": "/.github/workflows",
@@ -22,7 +22,7 @@ export const TEMPLATE = [
22
22
  {
23
23
  "location": "",
24
24
  "file": ".gitignore",
25
- "content": ".git/\r\nbin/\r\ndist/\r\nlib/\r\nnode_modules/\r\npackages/api/lib/\r\n\r\npackage-lock.json"
25
+ "content": ".git/\r\nbin/\r\ndist/\r\nlib/\r\nnode_modules/\r\npackages/api/lib/\r\n\r\npackage-lock.json\r\npnpm-lock.yaml"
26
26
  },
27
27
  {
28
28
  "location": "",
@@ -37,7 +37,7 @@ export const TEMPLATE = [
37
37
  {
38
38
  "location": "/.vscode",
39
39
  "file": "settings.json",
40
- "content": "{\r\n \"editor.tabSize\": 2,\r\n \"[typescript]\": {\r\n \"editor.defaultFormatter\": \"esbenp.prettier-vscode\",\r\n \"editor.formatOnSave\": true,\r\n \"editor.codeActionsOnSave\": {\r\n \"source.fixAll.eslint\": true\r\n },\r\n },\r\n \"[javascript]\": {\r\n \"editor.defaultFormatter\": \"esbenp.prettier-vscode\",\r\n \"editor.formatOnSave\": true,\r\n \"editor.codeActionsOnSave\": {\r\n \"source.fixAll.eslint\": true\r\n },\r\n }\r\n}"
40
+ "content": "{\r\n \"editor.tabSize\": 2,\r\n \"editor.formatOnSave\": true,\r\n \"[javascript][typescript]\": {\r\n \"editor.defaultFormatter\": \"esbenp.prettier-vscode\",\r\n \"editor.codeActionsOnSave\": {\r\n \"source.fixAll.eslint\": \"explicit\"\r\n },\r\n }\r\n}"
41
41
  },
42
42
  {
43
43
  "location": "",
@@ -52,7 +52,7 @@ export const TEMPLATE = [
52
52
  {
53
53
  "location": "",
54
54
  "file": "package.json",
55
- "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-template\"\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-template/issues\"\r\n },\r\n \"homepage\": \"https://github.com/samchon/nestia-template#readme\",\r\n \"devDependencies\": {\r\n \"@nestia/e2e\": \"^0.4.0\",\r\n \"@nestia/sdk\": \"^2.4.0\",\r\n \"@trivago/prettier-plugin-sort-imports\": \"^3.3.1\",\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\": \"^5.40.0\",\r\n \"@typescript-eslint/parser\": \"^5.40.0\",\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\": \"^1.4.1\",\r\n \"express\": \"^4.18.2\",\r\n \"nestia\": \"^5.1.2\",\r\n \"prettier\": \"^2.7.1\",\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.4.0\",\r\n \"@nestjs/common\": \"^10.2.10\",\r\n \"@nestjs/core\": \"^10.2.10\",\r\n \"@nestjs/platform-fastify\": \"^10.2.10\",\r\n \"dotenv\": \"^16.3.1\",\r\n \"dotenv-expand\": \"^10.0.0\",\r\n \"serialize-error\": \"^4.1.0\",\r\n \"tstl\": \"^2.5.13\",\r\n \"typia\": \"^5.3.0\",\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}"
55
+ "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.1\",\r\n \"@nestia/sdk\": \"^2.4.5\",\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.1.2\",\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.4.5\",\r\n \"@nestia/fetcher\": \"^2.4.5\",\r\n \"@nestjs/common\": \"^10.3.0\",\r\n \"@nestjs/core\": \"^10.3.0\",\r\n \"@nestjs/platform-fastify\": \"^10.3.0\",\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\": \"^2.5.13\",\r\n \"typia\": \"^5.3.12\",\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}"
56
56
  },
57
57
  {
58
58
  "location": "/packages/api",
@@ -67,7 +67,7 @@ export const TEMPLATE = [
67
67
  {
68
68
  "location": "/packages/api",
69
69
  "file": "package.json",
70
- "content": "{\r\n \"name\": \"@ORGANIZATION/PROJECT-api\",\r\n \"version\": \"0.1.0\",\r\n \"description\": \"SDK library generated by Nestia\",\r\n \"main\": \"lib/index.js\",\r\n \"typings\": \"lib/index.d.ts\",\r\n \"scripts\": {\r\n \"build\": \"npm run build:sdk && npm run compile\",\r\n \"build:sdk\": \"rimraf ../../src/api/functional && cd ../.. && npx nestia sdk && cd packages/api\",\r\n \"compile\": \"rimraf lib && tsc\",\r\n \"deploy\": \"npm run build && npm publish\",\r\n \"prepare\": \"ts-patch install && typia patch\"\r\n },\r\n \"repository\": {\r\n \"type\": \"git\",\r\n \"url\": \"https://github.com/samchon/nestia\"\r\n },\r\n \"author\": \"Jeongho Nam\",\r\n \"license\": \"MIT\",\r\n \"bugs\": {\r\n \"url\": \"https://github.com/samchon/nestia/issues\"\r\n },\r\n \"homepage\": \"https://nestia.io\",\r\n \"files\": [\r\n \"lib\",\r\n \"swagger.json\",\r\n \"package.json\",\r\n \"README.md\"\r\n ],\r\n \"devDependencies\": {\r\n \"rimraf\": \"^5.0.5\",\r\n \"ts-node\": \"^10.9.1\",\r\n \"ts-patch\": \"^3.0.2\",\r\n \"typescript\": \"^5.2.2\"\r\n },\r\n \"dependencies\": {\r\n \"@nestia/fetcher\": \"^2.4.0\",\r\n \"typia\": \"^5.3.0\"\r\n }\r\n}"
70
+ "content": "{\r\n \"name\": \"@ORGANIZATION/PROJECT-api\",\r\n \"version\": \"0.1.0\",\r\n \"description\": \"SDK library generated by Nestia\",\r\n \"main\": \"lib/index.js\",\r\n \"typings\": \"lib/index.d.ts\",\r\n \"scripts\": {\r\n \"build\": \"npm run build:sdk && npm run compile\",\r\n \"build:sdk\": \"rimraf ../../src/api/functional && cd ../.. && npx nestia sdk && cd packages/api\",\r\n \"compile\": \"rimraf lib && tsc\",\r\n \"deploy\": \"npm run build && npm publish\",\r\n \"prepare\": \"ts-patch install && typia patch\"\r\n },\r\n \"repository\": {\r\n \"type\": \"git\",\r\n \"url\": \"https://github.com/samchon/nestia\"\r\n },\r\n \"author\": \"Jeongho Nam\",\r\n \"license\": \"MIT\",\r\n \"bugs\": {\r\n \"url\": \"https://github.com/samchon/nestia/issues\"\r\n },\r\n \"homepage\": \"https://nestia.io\",\r\n \"files\": [\r\n \"lib\",\r\n \"swagger.json\",\r\n \"package.json\",\r\n \"README.md\"\r\n ],\r\n \"devDependencies\": {\r\n \"rimraf\": \"^5.0.5\",\r\n \"ts-node\": \"^10.9.2\",\r\n \"ts-patch\": \"^3.1.2\",\r\n \"typescript\": \"^5.3.3\"\r\n },\r\n \"dependencies\": {\r\n \"@nestia/fetcher\": \"^2.4.5\",\r\n \"typia\": \"^5.3.12\"\r\n }\r\n}"
71
71
  },
72
72
  {
73
73
  "location": "/packages/api",
@@ -82,12 +82,12 @@ export const TEMPLATE = [
82
82
  {
83
83
  "location": "",
84
84
  "file": "prettier.config.js",
85
- "content": "module.exports = {\r\n parser: \"typescript\",\r\n printWidth: 80,\r\n semi: true,\r\n tabWidth: 2,\r\n trailingComma: \"all\",\r\n importOrder: [\r\n \"<THIRD_PARTY_MODULES>\",\r\n \"^@ORGANIZATION/PROJECT-api(.*)$\",\r\n \"^[./]\",\r\n ],\r\n importOrderSeparation: true,\r\n importOrderSortSpecifiers: true,\r\n importOrderParserPlugins: [\"decorators-legacy\", \"typescript\"],\r\n};\r\n"
85
+ "content": "module.exports = {\r\n // DEFAULT CONFIGURATIONS\r\n parser: \"typescript\",\r\n printWidth: 80,\r\n semi: true,\r\n tabWidth: 2,\r\n trailingComma: \"all\",\r\n\r\n // PLUG-IN CONFIGURATIONS\r\n plugins: [\"@trivago/prettier-plugin-sort-imports\"],\r\n importOrder: [\r\n \"<THIRD_PARTY_MODULES>\",\r\n \"^@ORGANIZATION/PROJECT-api(.*)$\",\r\n \"^[./]\",\r\n ],\r\n importOrderSeparation: true,\r\n importOrderSortSpecifiers: true,\r\n importOrderParserPlugins: [\"decorators-legacy\", \"typescript\"],\r\n};\r\n"
86
86
  },
87
87
  {
88
88
  "location": "",
89
89
  "file": "README.md",
90
- "content": "# Nestia Template\r\n## Outline\r\n[![Build Status](https://github.com/samchon/nestia-template/workflows/build/badge.svg)](https://github.com/samchon/nestia-template/actions?query=workflow%3Abuild)\r\n\r\nA template repository for backend projects using [nestia](https://github.com/samchon/nestia).\r\n\r\nYou can create a new project from this boilerplate by running below command:\r\n\r\n```bash\r\nnpx nestia start <directory>\r\n```\r\n\r\nFor reference, this is a minimal boilerplate project concentrating only on [nestia](https://github.com/samchon/nestia) SDK generation. If you wanna much detailed boilerplate project even configured DB and Non-distruptive update system, visit [samchon/backend](https://github.com/samchon/backend) and create a new repository from that.\r\n\r\n\r\n\r\n\r\n## Directories and Files\r\nThis template project has categorized directories like below.\r\n\r\nAs you can see from the below, all of the Backend source files are placed into the [src](src/) directory. When you build the TypeScript source files, compiled files would be placed into the `lib` directory following the [tsconfig.json](tsconfig.json) configuration. Otherwise you build client [SDK](#32-sdk) library for npm publishing and their compiled files would be placed into the [packages](packages) directory.\r\n\r\n - [packages/api/](packages/api): SDK module built by `npm run build:api`\r\n - [src/](src): Backend source directory\r\n - [src/api/](src/api/): Client SDK that would be published to the `@ORGANIZATION/PROJECT-api`\r\n - [**src/api/functional/**](src/api/functional/): API functions generated by the [`nestia`](https://github.com/samchon/nestia)\r\n - [**src/api/structures/**](src/api/structures/): DTO structures\r\n - [src/controllers/](src/controllers/): Controller classes of the Main Program\r\n - [**test/**](test): Test Automation Program\r\n - [nestia.config.ts](nestia.config.ts): Configuration file of [`nestia`](https://github.com/samchon/nestia)\r\n - [package.json](package.json): NPM configuration\r\n - [tsconfig.json](tsconfig.json): TypeScript configuration for the main program\r\n - [tsconfig.api.json](tsconfig.api.json): TypeScript configuration for the SDK generation\r\n\r\n\r\n\r\n\r\n## NPM Run Commands\r\nList of the run commands defined in the [package.json](package.json) are like below:\r\n\r\n - `build`: Build everything\r\n - `build:api`: Build client SDK libray for the client developers\r\n - `build:main`: Build main program (`src` directory)\r\n - `build:sdk`: Build SDK into main program only\r\n - `build:swagger`: Build Swagger Documents\r\n - `build:test` Build test automation program (`test` directory)\r\n - `dev`: Incremental build for development (test program)\r\n - `eslint` & `eslint:fix` & `prettier`: Prettier and ESLint execution\r\n - `start`: Start local NestJS server\r\n - `test`: Run test automation program\r\n\r\n\r\n\r\n\r\n## Specialization\r\nTransform this template project to be yours.\r\n\r\nWhen you've created a new backend project through this template project, you can specialize it to be suitable for you by changing some words. Replace below words through IDE specific function like `Edit > Replace in Files` (*Ctrl + Shift + H*), who've been supported by the VSCode.\r\n\r\n| Before | After\r\n|-----------------|----------------------------------------\r\n| ORGANIZATION | Your account or corporation name\r\n| PROJECT | Your own project name\r\n| AUTHOR | Author name\r\n| https://github.com/samchon/nestia-template | Your repository URL\r\n\r\n\r\n\r\n\r\n## Test Driven Development\r\nWith [nestia](https://github.com/samchon/nestia) helps to accomplish TDD (Test Driven Development). \r\n\r\nJust define DTOs and API controllers' methods (only declarations) first. After the definitions, and build SDK (Software Development Kit) through [nestia](https://github.com/samchon/nestia) (`npm run build:sdk`). After buildling those SDK, develop test automation program using the SDK, following use-case scenarios in the framework of client side.\r\n\r\nDuring the test automation program development, you can find that which API is mis-designed or which requirement analysis is not exact. Development of the main program must be the last step after such validation process during TDD.\r\n\r\n> Visit the [samchon/backend](https://github.com/samchon/backend), then you may find much detailed story about this TDD.\r\n>\r\n> 1. Definitions\r\n> 2. SDK\r\n> 3. Test Automation Program\r\n> 4. Main Program\r\n\r\n```typescript\r\nimport typia from \"typia\";\r\n\r\nimport api from \"@ORGANIZATION/PROJECT-api/lib/index\";\r\nimport { IBbsArticle } from \"@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle\";\r\n\r\nimport { ArrayUtil } from \"../../../../utils/ArrayUtil\";\r\nimport { GaffComparator } from \"../../../internal/GaffComparator\";\r\nimport { RandomGenerator } from \"../../../internal/RandomGenerator\";\r\nimport { validate_index_sort } from \"../../../internal/validate_index_sort\";\r\n\r\nexport async function test_api_bbs_article_index_sort(\r\n connection: api.IConnection,\r\n): Promise<void> {\r\n // GENERATE 100 ARTICLES\r\n const section: string = \"general\";\r\n const articles: IBbsArticle[] = await ArrayUtil.asyncRepeat(100, () =>\r\n api.functional.bbs.articles.store(connection, section, {\r\n writer: RandomGenerator.name(),\r\n title: RandomGenerator.paragraph(),\r\n body: RandomGenerator.content(),\r\n format: \"txt\",\r\n files: [],\r\n password: RandomGenerator.alphabets(8),\r\n }),\r\n );\r\n typia.assertEquals(articles);\r\n\r\n // PREPARE VALIDATOR\r\n const validator = validate_index_sort(\"BbsArticleProvider.index()\")(\r\n (input: IBbsArticle.IRequest) =>\r\n api.functional.bbs.articles.index(connection, section, input),\r\n );\r\n\r\n // DO VALIDATE\r\n const components = [\r\n validator(\"created_at\")(GaffComparator.dates((x) => x.created_at)),\r\n validator(\"updated_at\")(GaffComparator.dates((x) => x.updated_at)),\r\n validator(\"title\")(GaffComparator.strings((x) => x.title)),\r\n validator(\"writer\")(GaffComparator.strings((x) => x.writer)),\r\n validator(\r\n \"writer\",\r\n \"title\",\r\n )(GaffComparator.strings((x) => [x.writer, x.title])),\r\n ];\r\n for (const comp of components) {\r\n await comp(\"+\");\r\n await comp(\"-\");\r\n }\r\n}\r\n```"
90
+ "content": "# Nestia Template\r\n## Outline\r\n[![Build Status](https://github.com/samchon/nestia-start/workflows/build/badge.svg)](https://github.com/samchon/nestia-start/actions?query=workflow%3Abuild)\r\n\r\nA template repository for backend projects using [nestia](https://github.com/samchon/nestia).\r\n\r\nYou can create a new project from this boilerplate by running below command:\r\n\r\n```bash\r\nnpx nestia start <directory>\r\n```\r\n\r\nFor reference, this is a minimal boilerplate project concentrating only on [nestia](https://github.com/samchon/nestia) SDK generation. \r\n\r\nIf you wanna much detailed boilerplate project, visit [`@samchon/backend`](https://github.com/samchon/backend).\r\n\r\n\r\n\r\n\r\n## Directories and Files\r\nThis template project has categorized directories like below.\r\n\r\nAs you can see from the below, all of the Backend source files are placed into the [src](src/) directory. When you build the TypeScript source files, compiled files would be placed into the `lib` directory following the [tsconfig.json](tsconfig.json) configuration. Otherwise you build client [SDK](#32-sdk) library for npm publishing and their compiled files would be placed into the [packages](packages) directory.\r\n\r\n - [packages/api/](packages/api): SDK module built by `npm run build:api`\r\n - [src/](src): Backend source directory\r\n - [src/api/](src/api/): Client SDK that would be published to the `@ORGANIZATION/PROJECT-api`\r\n - [**src/api/functional/**](src/api/functional/): API functions generated by the [`nestia`](https://github.com/samchon/nestia)\r\n - [**src/api/structures/**](src/api/structures/): DTO structures\r\n - [src/controllers/](src/controllers/): Controller classes of the Main Program\r\n - [**test/**](test): Test Automation Program\r\n - [nestia.config.ts](nestia.config.ts): Configuration file of [`nestia`](https://github.com/samchon/nestia)\r\n - [package.json](package.json): NPM configuration\r\n - [tsconfig.json](tsconfig.json): TypeScript configuration for the main program\r\n - [tsconfig.api.json](tsconfig.api.json): TypeScript configuration for the SDK generation\r\n\r\n\r\n\r\n\r\n## NPM Run Commands\r\nList of the run commands defined in the [package.json](package.json) are like below:\r\n\r\n - `build`: Build everything\r\n - `build:api`: Build client SDK libray for the client developers\r\n - `build:main`: Build main program (`src` directory)\r\n - `build:sdk`: Build SDK into main program only\r\n - `build:swagger`: Build Swagger Documents\r\n - `build:test` Build test automation program (`test` directory)\r\n - `dev`: Incremental build for development (test program)\r\n - `eslint` & `eslint:fix` & `prettier`: Prettier and ESLint execution\r\n - `start`: Start local NestJS server\r\n - `test`: Run test automation program\r\n\r\n\r\n\r\n\r\n## Specialization\r\nTransform this template project to be yours.\r\n\r\nWhen you've created a new backend project through this template project, you can specialize it to be suitable for you by changing some words. Replace below words through IDE specific function like `Edit > Replace in Files` (*Ctrl + Shift + H*), who've been supported by the VSCode.\r\n\r\n| Before | After\r\n|-----------------|----------------------------------------\r\n| ORGANIZATION | Your account or corporation name\r\n| PROJECT | Your own project name\r\n| AUTHOR | Author name\r\n| https://github.com/samchon/nestia-start | Your repository URL\r\n\r\n\r\n\r\n\r\n## Test Driven Development\r\nWith [nestia](https://github.com/samchon/nestia) helps to accomplish TDD (Test Driven Development). \r\n\r\nJust define DTOs and API controllers' methods (only declarations) first. After the definitions, and build SDK (Software Development Kit) through [nestia](https://github.com/samchon/nestia) (`npm run build:sdk`). After buildling those SDK, develop test automation program using the SDK, following use-case scenarios in the framework of client side.\r\n\r\nDuring the test automation program development, you can find that which API is mis-designed or which requirement analysis is not exact. Development of the main program must be the last step after such validation process during TDD.\r\n\r\n> Visit the [samchon/backend](https://github.com/samchon/backend), then you may find much detailed story about this TDD.\r\n>\r\n> 1. Definitions\r\n> 2. SDK\r\n> 3. Test Automation Program\r\n> 4. Main Program\r\n\r\n```typescript\r\nimport typia from \"typia\";\r\n\r\nimport api from \"@ORGANIZATION/PROJECT-api/lib/index\";\r\nimport { IBbsArticle } from \"@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle\";\r\n\r\nimport { ArrayUtil } from \"../../../../utils/ArrayUtil\";\r\nimport { GaffComparator } from \"../../../internal/GaffComparator\";\r\nimport { RandomGenerator } from \"../../../internal/RandomGenerator\";\r\nimport { validate_index_sort } from \"../../../internal/validate_index_sort\";\r\n\r\nexport async function test_api_bbs_article_index_sort(\r\n connection: api.IConnection,\r\n): Promise<void> {\r\n // GENERATE 100 ARTICLES\r\n const section: string = \"general\";\r\n const articles: IBbsArticle[] = await ArrayUtil.asyncRepeat(100, () =>\r\n api.functional.bbs.articles.store(connection, section, {\r\n writer: RandomGenerator.name(),\r\n title: RandomGenerator.paragraph(),\r\n body: RandomGenerator.content(),\r\n format: \"txt\",\r\n files: [],\r\n password: RandomGenerator.alphabets(8),\r\n }),\r\n );\r\n typia.assertEquals(articles);\r\n\r\n // PREPARE VALIDATOR\r\n const validator = validate_index_sort(\"BbsArticleProvider.index()\")(\r\n (input: IBbsArticle.IRequest) =>\r\n api.functional.bbs.articles.index(connection, section, input),\r\n );\r\n\r\n // DO VALIDATE\r\n const components = [\r\n validator(\"created_at\")(GaffComparator.dates((x) => x.created_at)),\r\n validator(\"updated_at\")(GaffComparator.dates((x) => x.updated_at)),\r\n validator(\"title\")(GaffComparator.strings((x) => x.title)),\r\n validator(\"writer\")(GaffComparator.strings((x) => x.writer)),\r\n validator(\r\n \"writer\",\r\n \"title\",\r\n )(GaffComparator.strings((x) => [x.writer, x.title])),\r\n ];\r\n for (const comp of components) {\r\n await comp(\"+\");\r\n await comp(\"-\");\r\n }\r\n}\r\n```"
91
91
  },
92
92
  {
93
93
  "location": "/src/executable",
@@ -107,7 +107,7 @@ export const TEMPLATE = [
107
107
  {
108
108
  "location": "/src",
109
109
  "file": "MyConfiguration.ts",
110
- "content": "import fs from \"fs\";\r\nimport path from \"path\";\r\n\r\nimport { MyGlobal } from \"./MyGlobal\";\r\n\r\nexport namespace MyConfiguration {\r\n export const API_PORT = () => Number(MyGlobal.env.PROJECT_API_PORT);\r\n\r\n export const ROOT = (() => {\r\n const splitted: string[] = __dirname.split(path.sep);\r\n return splitted.at(-1) === \"src\" && splitted.at(-2) === \"bin\"\r\n ? path.resolve(__dirname + \"/../..\")\r\n : fs.existsSync(__dirname + \"/.env\")\r\n ? __dirname\r\n : path.resolve(__dirname + \"/..\");\r\n })();\r\n}\r\n"
110
+ "content": "import fs from \"fs\";\r\nimport path from \"path\";\r\n\r\nimport { MyGlobal } from \"./MyGlobal\";\r\n\r\nexport namespace MyConfiguration {\r\n export const API_PORT = () => Number(MyGlobal.env.PROJECT_API_PORT);\r\n\r\n export const ROOT = (() => {\r\n const splitted: string[] = __dirname.split(path.sep);\r\n return splitted.at(-1) === \"src\" && splitted.at(-2) === \"bin\"\r\n ? path.resolve(__dirname + \"/../..\")\r\n : fs.existsSync(__dirname + \"/.env\")\r\n ? __dirname\r\n : path.resolve(__dirname + \"/..\");\r\n })();\r\n}\r\n"
111
111
  },
112
112
  {
113
113
  "location": "/src",
@@ -157,6 +157,6 @@ export const TEMPLATE = [
157
157
  {
158
158
  "location": "",
159
159
  "file": "webpack.config.js",
160
- "content": "const path = require(\"path\");\r\n\r\nconst CopyWebpackPlugin = require(\"copy-webpack-plugin\");\r\nconst WriteFilePlugin = require(\"write-file-webpack-plugin\");\r\nconst { IgnorePlugin } = require(\"webpack\");\r\n\r\nconst lazyImports = [\r\n \"@fastify/static\",\r\n \"@fastify/view\",\r\n \"@nestjs/microservices\",\r\n \"@nestjs/websockets\",\r\n \"class-transformer\",\r\n \"class-validator\",\r\n];\r\n\r\n// @reference https://tech-blog.s-yoshiki.com/entry/297\r\nmodule.exports = {\r\n // CUSTOMIZE HERE\r\n entry: {\r\n server: \"./src/executable/server.ts\",\r\n },\r\n output: {\r\n path: path.join(__dirname, \"dist\"),\r\n filename: \"[name].js\",\r\n },\r\n optimization: {\r\n minimize: true,\r\n },\r\n\r\n // JUST KEEP THEM\r\n mode: \"production\",\r\n target: \"node\",\r\n module: {\r\n rules: [\r\n {\r\n test: /\\.ts$/,\r\n exclude: /node_modules/,\r\n loader: \"ts-loader\",\r\n },\r\n ],\r\n },\r\n resolve: {\r\n extensions: [\".tsx\", \".ts\", \".js\"],\r\n },\r\n plugins: [\r\n new CopyWebpackPlugin({\r\n patterns: [\r\n {\r\n from: \".env\",\r\n to: \"[name][ext]\",\r\n },\r\n // {\r\n // from: \"./node_modules/.prisma/client/*.node\",\r\n // to: () => Promise.resolve(\"[path][name][ext]\"),\r\n // },\r\n ],\r\n }),\r\n new WriteFilePlugin(),\r\n new IgnorePlugin({\r\n checkResource: (resource) => {\r\n if (lazyImports.some((modulo) => resource.startsWith(modulo))) {\r\n try {\r\n require.resolve(resource);\r\n } catch (err) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n },\r\n }),\r\n ],\r\n};\r\n"
160
+ "content": "const path = require(\"path\");\r\n\r\nconst CopyWebpackPlugin = require(\"copy-webpack-plugin\");\r\nconst WriteFilePlugin = require(\"write-file-webpack-plugin\");\r\nconst { IgnorePlugin } = require(\"webpack\");\r\n\r\nconst lazyImports = [\r\n \"@fastify/static\",\r\n \"@fastify/view\",\r\n \"@nestjs/microservices\",\r\n \"@nestjs/websockets\",\r\n \"class-transformer\",\r\n \"class-validator\",\r\n];\r\n\r\n// @reference https://tech-blog.s-yoshiki.com/entry/297\r\nmodule.exports = {\r\n // CUSTOMIZE HERE\r\n entry: {\r\n server: \"./src/executable/server.ts\",\r\n },\r\n output: {\r\n path: path.join(__dirname, \"dist\"),\r\n filename: \"[name].js\",\r\n },\r\n optimization: {\r\n minimize: true,\r\n },\r\n\r\n // JUST KEEP THEM\r\n mode: \"production\",\r\n target: \"node\",\r\n module: {\r\n rules: [\r\n {\r\n test: /\\.ts$/,\r\n exclude: /node_modules/,\r\n loader: \"ts-loader\",\r\n },\r\n ],\r\n },\r\n resolve: {\r\n extensions: [\".tsx\", \".ts\", \".js\"],\r\n },\r\n plugins: [\r\n new CopyWebpackPlugin({\r\n patterns: [\r\n {\r\n from: \".env\",\r\n to: \"[name][ext]\",\r\n },\r\n // {\r\n // from: \"./node_modules/.prisma/client/*.node\",\r\n // to: () => Promise.resolve(\"[path][name][ext]\"),\r\n // globOptions: {\r\n // dot: true,\r\n // },\r\n // },\r\n ],\r\n }),\r\n new WriteFilePlugin(),\r\n new IgnorePlugin({\r\n checkResource: (resource) => {\r\n if (lazyImports.some((modulo) => resource.startsWith(modulo))) {\r\n try {\r\n require.resolve(resource);\r\n } catch (err) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n },\r\n }),\r\n ],\r\n};\r\n"
161
161
  }
162
162
  ]