@nestia/core 12.0.0 → 13.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.
Files changed (65) hide show
  1. package/lib/adaptors/McpAdaptor.js +21 -18
  2. package/lib/adaptors/McpAdaptor.js.map +1 -1
  3. package/lib/decorators/EncryptedBody.js +2 -7
  4. package/lib/decorators/EncryptedBody.js.map +1 -1
  5. package/lib/decorators/EncryptedRoute.js +1 -1
  6. package/lib/decorators/EncryptedRoute.js.map +1 -1
  7. package/lib/decorators/McpRoute.js +1 -1
  8. package/lib/decorators/McpRoute.js.map +1 -1
  9. package/lib/decorators/NoTransformConfigurationError.js +2 -2
  10. package/lib/decorators/PlainBody.js +2 -7
  11. package/lib/decorators/PlainBody.js.map +1 -1
  12. package/lib/decorators/SwaggerCustomizer.js +2 -2
  13. package/lib/decorators/SwaggerCustomizer.js.map +1 -1
  14. package/lib/decorators/SwaggerExample.d.ts +37 -40
  15. package/lib/decorators/SwaggerExample.js +24 -26
  16. package/lib/decorators/SwaggerExample.js.map +1 -1
  17. package/lib/decorators/TypedBody.js +3 -7
  18. package/lib/decorators/TypedBody.js.map +1 -1
  19. package/lib/decorators/TypedException.d.ts +1 -1
  20. package/lib/decorators/TypedFormData.js +2 -7
  21. package/lib/decorators/TypedFormData.js.map +1 -1
  22. package/lib/decorators/TypedQuery.js +2 -9
  23. package/lib/decorators/TypedQuery.js.map +1 -1
  24. package/lib/decorators/WebSocketRoute.js +1 -1
  25. package/lib/decorators/WebSocketRoute.js.map +1 -1
  26. package/lib/decorators/internal/get_path_and_querify.js +1 -1
  27. package/lib/decorators/internal/get_path_and_querify.js.map +1 -1
  28. package/lib/decorators/internal/is_media_type.d.ts +1 -0
  29. package/lib/decorators/internal/is_media_type.js +11 -0
  30. package/lib/decorators/internal/is_media_type.js.map +1 -0
  31. package/lib/decorators/internal/validate_request_query.js.map +1 -1
  32. package/native/go.mod +1 -1
  33. package/native/go.sum +32 -32
  34. package/native/plugin/plan.go +17 -0
  35. package/native/transform/build.go +41 -80
  36. package/native/transform/contributor.go +4 -64
  37. package/native/transform/core_transform.go +42 -705
  38. package/native/transform/node_transform.go +3 -5
  39. package/native/transform/printer.go +0 -217
  40. package/native/transform/run.go +29 -0
  41. package/native/transform/transform.go +54 -99
  42. package/native/transform/typia_dependency.go +117 -0
  43. package/native/transform/typia_fast.go +0 -320
  44. package/native/transform/typia_registry.go +40 -0
  45. package/package.json +12 -8
  46. package/src/adaptors/McpAdaptor.ts +29 -21
  47. package/src/decorators/EncryptedBody.ts +2 -9
  48. package/src/decorators/EncryptedRoute.ts +1 -1
  49. package/src/decorators/McpRoute.ts +2 -5
  50. package/src/decorators/NoTransformConfigurationError.ts +2 -2
  51. package/src/decorators/PlainBody.ts +2 -9
  52. package/src/decorators/SwaggerCustomizer.ts +4 -2
  53. package/src/decorators/SwaggerExample.ts +38 -41
  54. package/src/decorators/TypedBody.ts +5 -9
  55. package/src/decorators/TypedException.ts +1 -1
  56. package/src/decorators/TypedFormData.ts +7 -9
  57. package/src/decorators/TypedQuery.ts +7 -12
  58. package/src/decorators/WebSocketRoute.ts +1 -1
  59. package/src/decorators/internal/get_path_and_querify.ts +1 -1
  60. package/src/decorators/internal/is_media_type.ts +10 -0
  61. package/src/decorators/internal/validate_request_query.ts +2 -1
  62. package/native/transform/cleanup.go +0 -408
  63. package/native/transform/exports.go +0 -13
  64. package/native/transform/rewrite.go +0 -668
  65. package/native/transform/typia_replacement.go +0 -24
@@ -1,40 +1,38 @@
1
1
  /**
2
2
  * Attach example values to Swagger documents.
3
3
  *
4
- * `SwaggerExample` is a namespace of decorators that attach example values
5
- * to controller methods (request/response bodies, parameters), so that
4
+ * `SwaggerExample` is a namespace of decorators that attach example values to
5
+ * controller methods (request/response bodies, parameters), so that
6
6
  * `@nestia/sdk`'s Swagger generator can populate the `example` / `examples`
7
7
  * fields of the generated OpenAPI document.
8
8
  *
9
9
  * The decorators only affect Swagger document generation. They do not change
10
10
  * runtime behavior, validation, or SDK function signatures.
11
11
  *
12
+ * @author Jeongho Nam - https://github.com/samchon
12
13
  * @example
14
+ * ```typescript
15
+ * import core from "@nestia/core";
16
+ * import { Controller } from "@nestjs/common";
17
+ * import typia from "typia";
13
18
  *
14
- * ```typescript
15
- * import core from "@nestia/core";
16
- * import { Controller } from "@nestjs/common";
17
- * import typia from "typia";
18
- *
19
- * @Controller("bbs/articles")
20
- * export class BbsArticlesController {
21
- * // Single response example.
22
- * @core.SwaggerExample.Response(typia.random<IBbsArticle>())
23
- * @core.TypedRoute.Post()
24
- * public async create(
25
- * // Multiple named parameter examples plus a default one.
26
- * @core.SwaggerExample.Parameter(typia.random<IBbsArticle.ICreate>())
27
- * @core.SwaggerExample.Parameter("x", typia.random<IBbsArticle.ICreate>())
28
- * @core.SwaggerExample.Parameter("y", typia.random<IBbsArticle.ICreate>())
29
- * @core.TypedBody()
30
- * input: IBbsArticle.ICreate,
31
- * ): Promise<IBbsArticle> {
32
- * // ...
19
+ * @Controller("bbs/articles")
20
+ * export class BbsArticlesController {
21
+ * // Single response example.
22
+ * @core.SwaggerExample.Response(typia.random<IBbsArticle>())
23
+ * @core.TypedRoute.Post()
24
+ * public async create(
25
+ * // Multiple named parameter examples plus a default one.
26
+ * @core.SwaggerExample.Parameter(typia.random<IBbsArticle.ICreate>())
27
+ * @core.SwaggerExample.Parameter("x", typia.random<IBbsArticle.ICreate>())
28
+ * @core.SwaggerExample.Parameter("y", typia.random<IBbsArticle.ICreate>())
29
+ * @core.TypedBody()
30
+ * input: IBbsArticle.ICreate,
31
+ * ): Promise<IBbsArticle> {
32
+ * // ...
33
+ * }
33
34
  * }
34
- * }
35
- * ```
36
- *
37
- * @author Jeongho Nam - https://github.com/samchon
35
+ * ```;
38
36
  */
39
37
  export namespace SwaggerExample {
40
38
  /**
@@ -45,12 +43,12 @@ export namespace SwaggerExample {
45
43
  * - {@link Response | `Response(value)`}: registers `value` as the single
46
44
  * default `example`.
47
45
  * - {@link Response | `Response(key, value)`}: registers `value` under
48
- * `examples[key]`. May be applied multiple times to attach several
49
- * named examples.
46
+ * `examples[key]`. May be applied multiple times to attach several named
47
+ * examples.
50
48
  *
51
- * Both forms can coexist on the same method — the default `example` and
52
- * any number of named `examples` will all surface in the generated
53
- * Swagger document.
49
+ * Both forms can coexist on the same method — the default `example` and any
50
+ * number of named `examples` will all surface in the generated Swagger
51
+ * document.
54
52
  *
55
53
  * @template T Type of the example value (typically the route's response DTO).
56
54
  */
@@ -68,20 +66,19 @@ export namespace SwaggerExample {
68
66
  }
69
67
 
70
68
  /**
71
- * Attach an example value to a request parameter (body, path, query, etc.)
72
- * of a controller method.
69
+ * Attach an example value to a request parameter (body, path, query, etc.) of
70
+ * a controller method.
73
71
  *
74
72
  * Two forms are supported:
75
73
  *
76
74
  * - {@link Parameter | `Parameter(value)`}: registers `value` as the single
77
75
  * default `example` for that parameter.
78
76
  * - {@link Parameter | `Parameter(key, value)`}: registers `value` under
79
- * `examples[key]`. May be applied multiple times to attach several
80
- * named examples to the same parameter.
77
+ * `examples[key]`. May be applied multiple times to attach several named
78
+ * examples to the same parameter.
81
79
  *
82
- * Apply alongside the actual parameter decorator
83
- * (`@TypedBody()`, `@TypedParam()`, `@TypedQuery()`, etc.); decorator
84
- * order does not matter.
80
+ * Apply alongside the actual parameter decorator (`@TypedBody()`,
81
+ * `@TypedParam()`, `@TypedQuery()`, etc.); decorator order does not matter.
85
82
  *
86
83
  * @template T Type of the example value (typically the parameter's DTO).
87
84
  */
@@ -100,9 +97,9 @@ export namespace SwaggerExample {
100
97
  /**
101
98
  * Internal storage shape for `SwaggerExample` metadata.
102
99
  *
103
- * Reflects the OpenAPI spec's two example fields: a single default
104
- * `example`, and/or a named map `examples`. `index` is used internally
105
- * to associate parameter examples with the right parameter position.
100
+ * Reflects the OpenAPI spec's two example fields: a single default `example`,
101
+ * and/or a named map `examples`. `index` is used internally to associate
102
+ * parameter examples with the right parameter position.
106
103
  */
107
104
  export interface IData<T> {
108
105
  examples?: Record<string, T>;
@@ -163,7 +160,7 @@ const emplaceArrayOfParameters = (
163
160
  target: Object,
164
161
  propertyKey: string | symbol,
165
162
  ): SwaggerExample.IData<any>[] => {
166
- const array: SwaggerExample.IData<any>[] | undefined = Reflect.getMetadata(
163
+ const array: SwaggerExample.IData<any>[] | undefined = Reflect.getOwnMetadata(
167
164
  "nestia/SwaggerExample/Parameters",
168
165
  target,
169
166
  propertyKey,
@@ -7,6 +7,7 @@ import type express from "express";
7
7
  import type { FastifyRequest } from "fastify";
8
8
 
9
9
  import { IRequestBodyValidator } from "../options/IRequestBodyValidator";
10
+ import { is_media_type } from "./internal/is_media_type";
10
11
  import { is_request_body_undefined } from "./internal/is_request_body_undefined";
11
12
  import { validate_request_body } from "./internal/validate_request_body";
12
13
 
@@ -37,7 +38,10 @@ export function TypedBody<T>(
37
38
  .getRequest();
38
39
  if (is_request_body_undefined(request) && checker(undefined as T) === null)
39
40
  return undefined;
40
- else if (isApplicationJson(request.headers["content-type"]) === false)
41
+ else if (
42
+ is_media_type(request.headers["content-type"], "application/json") ===
43
+ false
44
+ )
41
45
  throw new BadRequestException(
42
46
  `Request body type is not "application/json".`,
43
47
  );
@@ -47,11 +51,3 @@ export function TypedBody<T>(
47
51
  return request.body;
48
52
  })();
49
53
  }
50
-
51
- /** @internal */
52
- const isApplicationJson = (text?: string): boolean =>
53
- text !== undefined &&
54
- text
55
- .split(";")
56
- .map((str) => str.trim())
57
- .some((str) => str === "application/json");
@@ -84,7 +84,7 @@ export function TypedException<T>(
84
84
  export function TypedException<T>(
85
85
  status: number | "2XX" | "3XX" | "4XX" | "5XX",
86
86
  description?: string | undefined,
87
- ): MethodDecorator;
87
+ ): MethodDecorator & Pick<TypedException.IProps<T>, never>;
88
88
 
89
89
  /** @internal */
90
90
  export function TypedException<T>(...args: any[]): MethodDecorator {
@@ -9,6 +9,7 @@ import type ExpressMulter from "multer";
9
9
 
10
10
  import type { IRequestFormDataProps } from "../options/IRequestFormDataProps";
11
11
  import { Singleton } from "../utils/Singleton";
12
+ import { is_media_type } from "./internal/is_media_type";
12
13
  import { validate_request_form_data } from "./internal/validate_request_form_data";
13
14
 
14
15
  /**
@@ -95,7 +96,12 @@ export namespace TypedFormData {
95
96
  ): Promise<T> {
96
97
  const http: HttpArgumentsHost = context.switchToHttp();
97
98
  const request: express.Request = http.getRequest();
98
- if (isMultipartFormData(request.headers["content-type"]) === false)
99
+ if (
100
+ is_media_type(
101
+ request.headers["content-type"],
102
+ "multipart/form-data",
103
+ ) === false
104
+ )
99
105
  throw new BadRequestException(
100
106
  `Request body type is not "multipart/form-data".`,
101
107
  );
@@ -177,11 +183,3 @@ const parseFiles =
177
183
  }),
178
184
  );
179
185
  };
180
-
181
- /** @internal */
182
- const isMultipartFormData = (text?: string): boolean =>
183
- text !== undefined &&
184
- text
185
- .split(";")
186
- .map((str) => str.trim())
187
- .some((str) => str === "multipart/form-data");
@@ -21,6 +21,7 @@ import typia from "typia";
21
21
  import { IRequestQueryValidator } from "../options/IRequestQueryValidator";
22
22
  import { IResponseBodyQuerifier } from "../options/IResponseBodyQuerifier";
23
23
  import { get_path_and_querify } from "./internal/get_path_and_querify";
24
+ import { is_media_type } from "./internal/is_media_type";
24
25
  import { route_error } from "./internal/route_error";
25
26
  import { validate_request_query } from "./internal/validate_request_query";
26
27
 
@@ -79,7 +80,12 @@ export namespace TypedQuery {
79
80
  const request: express.Request | FastifyRequest = context
80
81
  .switchToHttp()
81
82
  .getRequest();
82
- if (isApplicationQuery(request.headers["content-type"]) === false)
83
+ if (
84
+ is_media_type(
85
+ request.headers["content-type"],
86
+ "application/x-www-form-urlencoded",
87
+ ) === false
88
+ )
83
89
  throw new BadRequestException(
84
90
  `Request body type is not "application/x-www-form-urlencoded".`,
85
91
  );
@@ -172,17 +178,6 @@ function tail(url: string): string {
172
178
  return index === -1 ? "" : url.substring(index + 1);
173
179
  }
174
180
 
175
- /** @internal */
176
- function isApplicationQuery(text?: string): boolean {
177
- return (
178
- text !== undefined &&
179
- text
180
- .split(";")
181
- .map((str) => str.trim())
182
- .some((str) => str === "application/x-www-form-urlencoded")
183
- );
184
- }
185
-
186
181
  /** @internal */
187
182
  class FakeURLSearchParams {
188
183
  public constructor(private readonly target: Record<string, string[]>) {}
@@ -225,7 +225,7 @@ export namespace WebSocketRoute {
225
225
  value: IWebSocketRouteReflect.IArgument,
226
226
  ) => {
227
227
  const array: IWebSocketRouteReflect.IArgument[] | undefined =
228
- Reflect.getMetadata(
228
+ Reflect.getOwnMetadata(
229
229
  "nestia/WebSocketRoute/Parameters",
230
230
  target,
231
231
  propertyKey,
@@ -42,7 +42,7 @@ const take =
42
42
  const querify = (input: Record<string, any>): URLSearchParams => {
43
43
  const output: URLSearchParams = new URLSearchParams();
44
44
  for (const [key, value] of Object.entries(input))
45
- if (key === undefined) continue;
45
+ if (value === undefined) continue;
46
46
  else if (Array.isArray(value))
47
47
  for (const elem of value) output.append(key, String(elem));
48
48
  else output.append(key, String(value));
@@ -0,0 +1,10 @@
1
+ /** @internal */
2
+ export const is_media_type = (
3
+ text: string | undefined,
4
+ expected: string,
5
+ ): boolean =>
6
+ text !== undefined &&
7
+ text
8
+ .split(";")
9
+ .map((str) => str.trim().toLowerCase())
10
+ .some((str) => str === expected.toLowerCase());
@@ -35,7 +35,8 @@ const assert =
35
35
  message: MESSAGE,
36
36
  });
37
37
  }
38
- if (is_missing_query_property(exp)) return new BadRequestException(MESSAGE);
38
+ if (is_missing_query_property(exp))
39
+ return new BadRequestException(MESSAGE);
39
40
  throw exp;
40
41
  }
41
42
  };
@@ -1,408 +0,0 @@
1
- package transform
2
-
3
- import (
4
- "fmt"
5
- "sort"
6
- "strings"
7
- )
8
-
9
- func cleanupTransformedText(text string) string {
10
- return cleanupTransformedTextWithRuntimeAliases(text, nil)
11
- }
12
-
13
- func cleanupTransformedTextWithRuntimeAliases(text string, aliases []string) string {
14
- text = removeUnusedTypiaImports(text)
15
- if aliases == nil {
16
- return injectCleanupRuntimeImports(text)
17
- }
18
- return injectCleanupRuntimeImportsWithAliases(text, aliases)
19
- }
20
-
21
- func removeUnusedTypiaImports(text string) string {
22
- prefix := cleanupImportScanPrefix(text)
23
- if !strings.Contains(prefix, `require("typia")`) &&
24
- !strings.Contains(prefix, `require('typia')`) &&
25
- !strings.Contains(prefix, `from "typia"`) &&
26
- !strings.Contains(prefix, `from 'typia'`) {
27
- return text
28
- }
29
- type removal struct {
30
- start int
31
- end int
32
- }
33
- removals := []removal{}
34
- offset := 0
35
- for offset < len(prefix) {
36
- next := strings.IndexByte(prefix[offset:], '\n')
37
- lineEnd := len(prefix)
38
- nextOffset := len(prefix)
39
- if next >= 0 {
40
- lineEnd = offset + next
41
- nextOffset = lineEnd + 1
42
- }
43
- line := strings.TrimSuffix(prefix[offset:lineEnd], "\r")
44
- if alias, ok := unusedTypiaImportAlias(line); ok &&
45
- !cleanupIdentifierStillReferenced(text, alias, offset, nextOffset) {
46
- removals = append(removals, removal{start: offset, end: nextOffset})
47
- }
48
- offset = nextOffset
49
- }
50
- for i := len(removals) - 1; i >= 0; i-- {
51
- r := removals[i]
52
- text = text[:r.start] + text[r.end:]
53
- }
54
- return text
55
- }
56
-
57
- func unusedTypiaImportAlias(line string) (string, bool) {
58
- if strings.HasPrefix(line, "const ") {
59
- index := strings.Index(line, " = ")
60
- if index < 0 {
61
- return "", false
62
- }
63
- alias := strings.TrimSpace(strings.TrimPrefix(line[:index], "const "))
64
- if isTypiaImportAlias(alias) == false {
65
- return "", false
66
- }
67
- value := strings.TrimSpace(line[index+3:])
68
- return alias, value == `__importDefault(require("typia"));` ||
69
- value == `__importDefault(require('typia'));` ||
70
- value == `require("typia");` ||
71
- value == `require('typia');`
72
- }
73
- if strings.HasPrefix(line, "import ") && strings.HasSuffix(line, ` from "typia";`) {
74
- alias := strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(line, "import "), ` from "typia";`))
75
- return alias, isTypiaImportAlias(alias)
76
- }
77
- if strings.HasPrefix(line, "import ") && strings.HasSuffix(line, ` from 'typia';`) {
78
- alias := strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(line, "import "), ` from 'typia';`))
79
- return alias, isTypiaImportAlias(alias)
80
- }
81
- return "", false
82
- }
83
-
84
- func isTypiaImportAlias(alias string) bool {
85
- if alias == "typia" {
86
- return true
87
- }
88
- if !strings.HasPrefix(alias, "typia_") {
89
- return false
90
- }
91
- for _, ch := range alias[len("typia_"):] {
92
- if ch < '0' || ch > '9' {
93
- return false
94
- }
95
- }
96
- return len(alias) > len("typia_")
97
- }
98
-
99
- func cleanupIdentifierStillReferenced(text, alias string, lineStart, lineEnd int) bool {
100
- return cleanupIdentifierAppears(text[:lineStart], alias) ||
101
- cleanupIdentifierAppears(text[lineEnd:], alias)
102
- }
103
-
104
- func cleanupIdentifierAppears(text string, alias string) bool {
105
- offset := 0
106
- for {
107
- index := strings.Index(text[offset:], alias)
108
- if index < 0 {
109
- return false
110
- }
111
- start := offset + index
112
- end := start + len(alias)
113
- if (start == 0 || !isCleanupRuntimeAliasIdentifierByte(text[start-1])) &&
114
- (end == len(text) || !isCleanupRuntimeAliasIdentifierByte(text[end])) {
115
- return true
116
- }
117
- offset = end
118
- }
119
- }
120
-
121
- func injectCleanupRuntimeImports(text string) string {
122
- aliases := collectCleanupRuntimeAliases(text)
123
- return injectCleanupRuntimeImportsWithAliases(text, aliases)
124
- }
125
-
126
- func injectCleanupRuntimeImportsWithAliases(text string, aliases []string) string {
127
- if len(aliases) == 0 {
128
- return text
129
- }
130
- esModule := isCleanupESModuleOutput(text)
131
- existing := collectExistingCleanupRuntimeImports(text)
132
- imports := make([]string, 0, len(aliases))
133
- for _, alias := range aliases {
134
- module := cleanupRuntimeModuleOf(alias)
135
- if existing[cleanupRuntimeImportKey(alias, module)] {
136
- continue
137
- }
138
- name := cleanupRuntimeNameOf(alias)
139
- if esModule {
140
- imports = append(imports, fmt.Sprintf(`import { %s as %s } from %q;`, name, alias, module))
141
- } else {
142
- imports = append(imports, fmt.Sprintf(`const { %s: %s } = require(%q);`, name, alias, module))
143
- }
144
- }
145
- if len(imports) == 0 {
146
- return text
147
- }
148
- index := cleanupRuntimeImportInsertionIndex(text, esModule)
149
- return text[:index] + strings.Join(imports, "\n") + "\n" + text[index:]
150
- }
151
-
152
- func collectCleanupRuntimeAliases(text string) []string {
153
- seen := map[string]bool{}
154
- offset := 0
155
- for {
156
- index := strings.Index(text[offset:], cleanupRuntimeAliasPrefix)
157
- if index < 0 {
158
- break
159
- }
160
- start := offset + index
161
- end := start + len(cleanupRuntimeAliasPrefix)
162
- if start != 0 && isCleanupRuntimeAliasIdentifierByte(text[start-1]) {
163
- offset = end
164
- continue
165
- }
166
- for end < len(text) && isCleanupRuntimeAliasNameByte(text[end]) {
167
- end++
168
- }
169
- if end != start+len(cleanupRuntimeAliasPrefix) &&
170
- (end == len(text) || !isCleanupRuntimeAliasIdentifierByte(text[end])) {
171
- seen[text[start:end]] = true
172
- }
173
- offset = end
174
- }
175
- return sortCleanupRuntimeAliases(seen)
176
- }
177
-
178
- func sortCleanupRuntimeAliases(seen map[string]bool) []string {
179
- aliases := make([]string, 0, len(seen))
180
- for alias := range seen {
181
- aliases = append(aliases, alias)
182
- }
183
- sort.SliceStable(aliases, func(i, j int) bool {
184
- left, right := cleanupRuntimeAliasRank(aliases[i]), cleanupRuntimeAliasRank(aliases[j])
185
- if left != right {
186
- return left < right
187
- }
188
- return aliases[i] < aliases[j]
189
- })
190
- return aliases
191
- }
192
-
193
- const cleanupRuntimeAliasPrefix = "__typia_transform_"
194
-
195
- func isCleanupRuntimeAliasNameByte(value byte) bool {
196
- return (value >= 'A' && value <= 'Z') ||
197
- (value >= 'a' && value <= 'z') ||
198
- (value >= '0' && value <= '9') ||
199
- value == '_'
200
- }
201
-
202
- func isCleanupRuntimeAliasIdentifierByte(value byte) bool {
203
- return isCleanupRuntimeAliasNameByte(value) || value == '$'
204
- }
205
-
206
- func cleanupRuntimeAliasRank(alias string) int {
207
- name := strings.TrimPrefix(alias, cleanupRuntimeAliasPrefix)
208
- switch {
209
- case strings.HasPrefix(name, "_is"):
210
- return 100
211
- case strings.HasPrefix(name, "_assert"):
212
- return 150
213
- case strings.HasPrefix(name, "_randomFormat"):
214
- return 200
215
- case name == "_randomString":
216
- return 210
217
- case name == "_randomInteger":
218
- return 220
219
- case name == "_randomNumber":
220
- return 221
221
- case strings.HasPrefix(name, "_random"):
222
- return 230
223
- case name == "_validateReport":
224
- return 800
225
- case name == "_createStandardSchema":
226
- return 900
227
- }
228
- return 500
229
- }
230
-
231
- func cleanupRuntimeModuleOf(alias string) string {
232
- return "typia/lib/internal/" + cleanupRuntimeNameOf(alias)
233
- }
234
-
235
- func cleanupRuntimeNameOf(alias string) string {
236
- name := strings.TrimPrefix(alias, cleanupRuntimeAliasPrefix)
237
- if !strings.HasPrefix(name, "_") {
238
- name = "_" + name
239
- }
240
- return name
241
- }
242
-
243
- func collectExistingCleanupRuntimeImports(text string) map[string]bool {
244
- output := map[string]bool{}
245
- forEachCleanupImportLine(cleanupImportScanPrefix(text), func(line string) {
246
- if !strings.Contains(line, cleanupRuntimeAliasPrefix) ||
247
- !strings.Contains(line, "typia/lib/internal/") {
248
- return
249
- }
250
- alias, ok := cleanupRuntimeImportAlias(line)
251
- if !ok {
252
- return
253
- }
254
- module, ok := cleanupRuntimeImportModule(line)
255
- if !ok {
256
- return
257
- }
258
- output[cleanupRuntimeImportKey(alias, module)] = true
259
- })
260
- return output
261
- }
262
-
263
- func cleanupImportScanPrefix(text string) string {
264
- const limit = 1 << 20
265
- if len(text) <= limit {
266
- return text
267
- }
268
- return text[:limit]
269
- }
270
-
271
- func cleanupRuntimeImportKey(alias string, module string) string {
272
- return alias + "\x00" + module
273
- }
274
-
275
- func forEachCleanupImportLine(text string, iterate func(string)) {
276
- for len(text) > 0 {
277
- next := strings.IndexByte(text, '\n')
278
- line := text
279
- if next >= 0 {
280
- line = text[:next]
281
- text = text[next+1:]
282
- } else {
283
- text = ""
284
- }
285
- iterate(strings.TrimSuffix(line, "\r"))
286
- }
287
- }
288
-
289
- func cleanupRuntimeImportAlias(line string) (string, bool) {
290
- switch {
291
- case strings.HasPrefix(line, "const { "):
292
- colon := strings.IndexByte(line, ':')
293
- if colon < 0 {
294
- return "", false
295
- }
296
- end := strings.IndexByte(line[colon+1:], '}')
297
- if end < 0 {
298
- return "", false
299
- }
300
- return cleanupFirstIdentifier(strings.TrimSpace(line[colon+1 : colon+1+end]))
301
- case strings.HasPrefix(line, "import { "):
302
- index := strings.LastIndex(line, " as ")
303
- if index < 0 {
304
- return "", false
305
- }
306
- end := strings.IndexByte(line[index+4:], '}')
307
- if end < 0 {
308
- return "", false
309
- }
310
- return cleanupFirstIdentifier(strings.TrimSpace(line[index+4 : index+4+end]))
311
- case strings.HasPrefix(line, "const "):
312
- rest := strings.TrimPrefix(line, "const ")
313
- index := strings.Index(rest, " = require(")
314
- if index < 0 {
315
- return "", false
316
- }
317
- return cleanupFirstIdentifier(rest[:index])
318
- case strings.HasPrefix(line, "import * as "):
319
- rest := strings.TrimPrefix(line, "import * as ")
320
- index := strings.Index(rest, " from ")
321
- if index < 0 {
322
- return "", false
323
- }
324
- return cleanupFirstIdentifier(rest[:index])
325
- default:
326
- return "", false
327
- }
328
- }
329
-
330
- func cleanupFirstIdentifier(text string) (string, bool) {
331
- fields := strings.Fields(text)
332
- if len(fields) == 0 {
333
- return "", false
334
- }
335
- name := strings.Trim(fields[0], " \t{}")
336
- if !strings.HasPrefix(name, cleanupRuntimeAliasPrefix) {
337
- return "", false
338
- }
339
- return name, true
340
- }
341
-
342
- func cleanupRuntimeImportModule(line string) (string, bool) {
343
- for _, marker := range []string{`require("`, `require('`, ` from "`, ` from '`} {
344
- index := strings.LastIndex(line, marker)
345
- if index < 0 {
346
- continue
347
- }
348
- start := index + len(marker)
349
- quote := marker[len(marker)-1]
350
- end := strings.IndexByte(line[start:], quote)
351
- if end < 0 {
352
- return "", false
353
- }
354
- module := line[start : start+end]
355
- if !strings.HasPrefix(module, "typia/lib/internal/") {
356
- return "", false
357
- }
358
- return module, true
359
- }
360
- return "", false
361
- }
362
-
363
- func isCleanupESModuleOutput(text string) bool {
364
- output := false
365
- forEachCleanupImportLine(cleanupImportScanPrefix(text), func(line string) {
366
- if strings.HasPrefix(line, "import ") ||
367
- strings.HasPrefix(line, "import{") ||
368
- strings.HasPrefix(line, "import*") ||
369
- strings.HasPrefix(line, "export ") {
370
- output = true
371
- }
372
- })
373
- return output
374
- }
375
-
376
- func cleanupRuntimeImportInsertionIndex(text string, esModule bool) int {
377
- index := 0
378
- if strings.HasPrefix(text, "#!") {
379
- if next := strings.IndexByte(text, '\n'); next >= 0 {
380
- index = next + 1
381
- } else {
382
- return len(text)
383
- }
384
- }
385
- if esModule {
386
- return index
387
- }
388
- for {
389
- next := consumeCleanupRuntimeImportPrefix(text[index:])
390
- if next == 0 {
391
- return index
392
- }
393
- index += next
394
- }
395
- }
396
-
397
- func consumeCleanupRuntimeImportPrefix(text string) int {
398
- for _, prefix := range []string{
399
- "\"use strict\";\n",
400
- "'use strict';\n",
401
- "/* @ttsc-rewritten */\n",
402
- } {
403
- if strings.HasPrefix(text, prefix) {
404
- return len(prefix)
405
- }
406
- }
407
- return 0
408
- }