@nestia/core 11.2.0 → 12.0.0-dev.20260520.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 (171) hide show
  1. package/MIGRATION.md +169 -0
  2. package/lib/adaptors/WebSocketAdaptor.js +7 -3
  3. package/lib/adaptors/WebSocketAdaptor.js.map +1 -1
  4. package/lib/decorators/DynamicModule.js.map +1 -1
  5. package/lib/decorators/EncryptedBody.js.map +1 -1
  6. package/lib/decorators/EncryptedController.js.map +1 -1
  7. package/lib/decorators/EncryptedModule.js.map +1 -1
  8. package/lib/decorators/EncryptedRoute.js.map +1 -1
  9. package/lib/decorators/HumanRoute.js.map +1 -1
  10. package/lib/decorators/NoTransformConfigurationError.js +5 -2
  11. package/lib/decorators/NoTransformConfigurationError.js.map +1 -1
  12. package/lib/decorators/PlainBody.js.map +1 -1
  13. package/lib/decorators/SwaggerCustomizer.js.map +1 -1
  14. package/lib/decorators/SwaggerExample.js.map +1 -1
  15. package/lib/decorators/TypedBody.js.map +1 -1
  16. package/lib/decorators/TypedException.js.map +1 -1
  17. package/lib/decorators/TypedFormData.js.map +1 -1
  18. package/lib/decorators/TypedHeaders.js.map +1 -1
  19. package/lib/decorators/TypedParam.js +5 -2
  20. package/lib/decorators/TypedParam.js.map +1 -1
  21. package/lib/decorators/TypedQuery.js.map +1 -1
  22. package/lib/decorators/TypedRoute.js.map +1 -1
  23. package/lib/decorators/WebSocketRoute.js.map +1 -1
  24. package/lib/decorators/doNotThrowTransformError.js.map +1 -1
  25. package/lib/decorators/internal/get_path_and_querify.js +5 -2
  26. package/lib/decorators/internal/get_path_and_querify.js.map +1 -1
  27. package/lib/decorators/internal/get_path_and_stringify.js +5 -2
  28. package/lib/decorators/internal/get_path_and_stringify.js.map +1 -1
  29. package/lib/decorators/internal/get_text_body.js.map +1 -1
  30. package/lib/decorators/internal/headers_to_object.js.map +1 -1
  31. package/lib/decorators/internal/is_request_body_undefined.js.map +1 -1
  32. package/lib/decorators/internal/load_controller.js +48 -16
  33. package/lib/decorators/internal/load_controller.js.map +1 -1
  34. package/lib/decorators/internal/route_error.js +3 -3
  35. package/lib/decorators/internal/route_error.js.map +1 -1
  36. package/lib/decorators/internal/validate_request_body.js +5 -2
  37. package/lib/decorators/internal/validate_request_body.js.map +1 -1
  38. package/lib/decorators/internal/validate_request_form_data.js +5 -2
  39. package/lib/decorators/internal/validate_request_form_data.js.map +1 -1
  40. package/lib/decorators/internal/validate_request_headers.js +5 -2
  41. package/lib/decorators/internal/validate_request_headers.js.map +1 -1
  42. package/lib/decorators/internal/validate_request_query.js +30 -4
  43. package/lib/decorators/internal/validate_request_query.js.map +1 -1
  44. package/lib/index.js.map +1 -1
  45. package/lib/transform.d.ts +2 -5
  46. package/lib/transform.js +48 -28
  47. package/lib/transform.js.map +1 -1
  48. package/lib/utils/ArrayUtil.js.map +1 -1
  49. package/lib/utils/ExceptionManager.js.map +1 -1
  50. package/lib/utils/Singleton.js.map +1 -1
  51. package/lib/utils/SourceFinder.js.map +1 -1
  52. package/lib/utils/VersioningStrategy.js.map +1 -1
  53. package/native/cmd/ttsc-nestia/build.go +434 -0
  54. package/native/cmd/ttsc-nestia/cleanup.go +408 -0
  55. package/native/cmd/ttsc-nestia/core_querify.go +227 -0
  56. package/native/cmd/ttsc-nestia/core_transform.go +1713 -0
  57. package/native/cmd/ttsc-nestia/core_websocket.go +115 -0
  58. package/native/cmd/ttsc-nestia/main.go +72 -0
  59. package/native/cmd/ttsc-nestia/path_rewrite.go +285 -0
  60. package/native/cmd/ttsc-nestia/printer.go +244 -0
  61. package/native/cmd/ttsc-nestia/rewrite.go +662 -0
  62. package/native/cmd/ttsc-nestia/sdk_metadata_json.go +327 -0
  63. package/native/cmd/ttsc-nestia/sdk_transform.go +1541 -0
  64. package/native/cmd/ttsc-nestia/transform.go +387 -0
  65. package/native/cmd/ttsc-nestia/typia_fast.go +326 -0
  66. package/native/cmd/ttsc-nestia/typia_replacement.go +24 -0
  67. package/native/go.mod +32 -0
  68. package/native/go.sum +54 -0
  69. package/native/plugin/plan.go +102 -0
  70. package/native/transform.cjs +21 -0
  71. package/package.json +27 -22
  72. package/src/decorators/NoTransformConfigurationError.ts +5 -2
  73. package/src/decorators/internal/load_controller.ts +50 -19
  74. package/src/decorators/internal/validate_request_query.ts +21 -2
  75. package/src/transform.ts +82 -35
  76. package/lib/decorators/internal/NoTransformConfigureError.d.ts +0 -1
  77. package/lib/decorators/internal/NoTransformConfigureError.js +0 -7
  78. package/lib/decorators/internal/NoTransformConfigureError.js.map +0 -1
  79. package/lib/options/INestiaTransformOptions.d.ts +0 -13
  80. package/lib/options/INestiaTransformOptions.js +0 -3
  81. package/lib/options/INestiaTransformOptions.js.map +0 -1
  82. package/lib/options/INestiaTransformProject.d.ts +0 -5
  83. package/lib/options/INestiaTransformProject.js +0 -3
  84. package/lib/options/INestiaTransformProject.js.map +0 -1
  85. package/lib/programmers/PlainBodyProgrammer.d.ts +0 -9
  86. package/lib/programmers/PlainBodyProgrammer.js +0 -58
  87. package/lib/programmers/PlainBodyProgrammer.js.map +0 -1
  88. package/lib/programmers/TypedBodyProgrammer.d.ts +0 -9
  89. package/lib/programmers/TypedBodyProgrammer.js +0 -94
  90. package/lib/programmers/TypedBodyProgrammer.js.map +0 -1
  91. package/lib/programmers/TypedFormDataBodyProgrammer.d.ts +0 -9
  92. package/lib/programmers/TypedFormDataBodyProgrammer.js +0 -65
  93. package/lib/programmers/TypedFormDataBodyProgrammer.js.map +0 -1
  94. package/lib/programmers/TypedHeadersProgrammer.d.ts +0 -9
  95. package/lib/programmers/TypedHeadersProgrammer.js +0 -38
  96. package/lib/programmers/TypedHeadersProgrammer.js.map +0 -1
  97. package/lib/programmers/TypedParamProgrammer.d.ts +0 -10
  98. package/lib/programmers/TypedParamProgrammer.js +0 -32
  99. package/lib/programmers/TypedParamProgrammer.js.map +0 -1
  100. package/lib/programmers/TypedQueryBodyProgrammer.d.ts +0 -9
  101. package/lib/programmers/TypedQueryBodyProgrammer.js +0 -74
  102. package/lib/programmers/TypedQueryBodyProgrammer.js.map +0 -1
  103. package/lib/programmers/TypedQueryProgrammer.d.ts +0 -9
  104. package/lib/programmers/TypedQueryProgrammer.js +0 -75
  105. package/lib/programmers/TypedQueryProgrammer.js.map +0 -1
  106. package/lib/programmers/TypedQueryRouteProgrammer.d.ts +0 -9
  107. package/lib/programmers/TypedQueryRouteProgrammer.js +0 -75
  108. package/lib/programmers/TypedQueryRouteProgrammer.js.map +0 -1
  109. package/lib/programmers/TypedRouteProgrammer.d.ts +0 -9
  110. package/lib/programmers/TypedRouteProgrammer.js +0 -79
  111. package/lib/programmers/TypedRouteProgrammer.js.map +0 -1
  112. package/lib/programmers/http/HttpAssertQuerifyProgrammer.d.ts +0 -9
  113. package/lib/programmers/http/HttpAssertQuerifyProgrammer.js +0 -39
  114. package/lib/programmers/http/HttpAssertQuerifyProgrammer.js.map +0 -1
  115. package/lib/programmers/http/HttpIsQuerifyProgrammer.d.ts +0 -9
  116. package/lib/programmers/http/HttpIsQuerifyProgrammer.js +0 -36
  117. package/lib/programmers/http/HttpIsQuerifyProgrammer.js.map +0 -1
  118. package/lib/programmers/http/HttpQuerifyProgrammer.d.ts +0 -9
  119. package/lib/programmers/http/HttpQuerifyProgrammer.js +0 -50
  120. package/lib/programmers/http/HttpQuerifyProgrammer.js.map +0 -1
  121. package/lib/programmers/http/HttpValidateQuerifyProgrammer.d.ts +0 -9
  122. package/lib/programmers/http/HttpValidateQuerifyProgrammer.js +0 -40
  123. package/lib/programmers/http/HttpValidateQuerifyProgrammer.js.map +0 -1
  124. package/lib/programmers/internal/CoreMetadataUtil.d.ts +0 -5
  125. package/lib/programmers/internal/CoreMetadataUtil.js +0 -19
  126. package/lib/programmers/internal/CoreMetadataUtil.js.map +0 -1
  127. package/lib/transformers/FileTransformer.d.ts +0 -5
  128. package/lib/transformers/FileTransformer.js +0 -80
  129. package/lib/transformers/FileTransformer.js.map +0 -1
  130. package/lib/transformers/MethodTransformer.d.ts +0 -8
  131. package/lib/transformers/MethodTransformer.js +0 -58
  132. package/lib/transformers/MethodTransformer.js.map +0 -1
  133. package/lib/transformers/NodeTransformer.d.ts +0 -8
  134. package/lib/transformers/NodeTransformer.js +0 -24
  135. package/lib/transformers/NodeTransformer.js.map +0 -1
  136. package/lib/transformers/ParameterDecoratorTransformer.d.ts +0 -9
  137. package/lib/transformers/ParameterDecoratorTransformer.js +0 -104
  138. package/lib/transformers/ParameterDecoratorTransformer.js.map +0 -1
  139. package/lib/transformers/ParameterTransformer.d.ts +0 -8
  140. package/lib/transformers/ParameterTransformer.js +0 -37
  141. package/lib/transformers/ParameterTransformer.js.map +0 -1
  142. package/lib/transformers/TypedRouteTransformer.d.ts +0 -9
  143. package/lib/transformers/TypedRouteTransformer.js +0 -68
  144. package/lib/transformers/TypedRouteTransformer.js.map +0 -1
  145. package/lib/transformers/WebSocketRouteTransformer.d.ts +0 -9
  146. package/lib/transformers/WebSocketRouteTransformer.js +0 -72
  147. package/lib/transformers/WebSocketRouteTransformer.js.map +0 -1
  148. package/src/decorators/internal/NoTransformConfigureError.ts +0 -2
  149. package/src/options/INestiaTransformOptions.ts +0 -34
  150. package/src/options/INestiaTransformProject.ts +0 -10
  151. package/src/programmers/PlainBodyProgrammer.ts +0 -72
  152. package/src/programmers/TypedBodyProgrammer.ts +0 -148
  153. package/src/programmers/TypedFormDataBodyProgrammer.ts +0 -118
  154. package/src/programmers/TypedHeadersProgrammer.ts +0 -65
  155. package/src/programmers/TypedParamProgrammer.ts +0 -33
  156. package/src/programmers/TypedQueryBodyProgrammer.ts +0 -113
  157. package/src/programmers/TypedQueryProgrammer.ts +0 -115
  158. package/src/programmers/TypedQueryRouteProgrammer.ts +0 -107
  159. package/src/programmers/TypedRouteProgrammer.ts +0 -103
  160. package/src/programmers/http/HttpAssertQuerifyProgrammer.ts +0 -74
  161. package/src/programmers/http/HttpIsQuerifyProgrammer.ts +0 -77
  162. package/src/programmers/http/HttpQuerifyProgrammer.ts +0 -110
  163. package/src/programmers/http/HttpValidateQuerifyProgrammer.ts +0 -78
  164. package/src/programmers/internal/CoreMetadataUtil.ts +0 -21
  165. package/src/transformers/FileTransformer.ts +0 -109
  166. package/src/transformers/MethodTransformer.ts +0 -103
  167. package/src/transformers/NodeTransformer.ts +0 -23
  168. package/src/transformers/ParameterDecoratorTransformer.ts +0 -143
  169. package/src/transformers/ParameterTransformer.ts +0 -57
  170. package/src/transformers/TypedRouteTransformer.ts +0 -85
  171. package/src/transformers/WebSocketRouteTransformer.ts +0 -120
@@ -0,0 +1,387 @@
1
+ package main
2
+
3
+ import (
4
+ "bytes"
5
+ "encoding/json"
6
+ "flag"
7
+ "fmt"
8
+ "os"
9
+ "path/filepath"
10
+ "regexp"
11
+ "sort"
12
+ "strings"
13
+
14
+ "github.com/samchon/nestia/packages/core/native/plugin"
15
+ "github.com/samchon/ttsc/packages/ttsc/driver"
16
+ typiaadapter "github.com/samchon/typia/packages/typia/native/adapter"
17
+ )
18
+
19
+ type transformProjectOutput struct {
20
+ Diagnostics []transformCompilerDiagnostic `json:"diagnostics,omitempty"`
21
+ TypeScript map[string]string `json:"typescript"`
22
+ }
23
+
24
+ type transformCompilerDiagnostic struct {
25
+ File *string `json:"file"`
26
+ Category string `json:"category"`
27
+ Code string `json:"code"`
28
+ Line int `json:"line,omitempty"`
29
+ Character int `json:"character,omitempty"`
30
+ MessageText string `json:"messageText"`
31
+ }
32
+
33
+ func runTransform(args []string) int {
34
+ fs := flag.NewFlagSet("transform", flag.ContinueOnError)
35
+ fs.SetOutput(stderr)
36
+ file := fs.String("file", "", "absolute or cwd-relative path of the .ts file to transform")
37
+ tsconfigPath := fs.String("tsconfig", "tsconfig.json", "tsconfig.json owning --file")
38
+ cwdOverride := fs.String("cwd", "", "override the working directory")
39
+ out := fs.String("out", "", "write output to PATH")
40
+ output := fs.String("output", "ts", "transform output kind: ts")
41
+ pluginsJSON := fs.String("plugins-json", "", "ordered ttsc plugin payload")
42
+ if err := fs.Parse(args); err != nil {
43
+ return 2
44
+ }
45
+ if *output != "ts" {
46
+ fmt.Fprintf(stderr, "ttsc-nestia transform: unknown --output value %q\n", *output)
47
+ return 2
48
+ }
49
+ plan, err := plugin.ParsePlan(*pluginsJSON)
50
+ if err != nil {
51
+ fmt.Fprintf(stderr, "ttsc-nestia transform: %v\n", err)
52
+ return 2
53
+ }
54
+
55
+ cwd, ok := resolveCWD("ttsc-nestia transform", *cwdOverride)
56
+ if !ok {
57
+ return 2
58
+ }
59
+ prog, diags, err := driver.LoadProgram(cwd, *tsconfigPath, driver.LoadProgramOptions{
60
+ ForceNoEmit: true,
61
+ })
62
+ if err != nil {
63
+ fmt.Fprintf(stderr, "ttsc-nestia transform: %v\n", err)
64
+ return 2
65
+ }
66
+ if len(diags) > 0 {
67
+ driver.WritePrettyDiagnostics(stderr, diags, cwd)
68
+ return 2
69
+ }
70
+ defer prog.Close()
71
+
72
+ if *file == "" {
73
+ if *out != "" {
74
+ fmt.Fprintln(stderr, "ttsc-nestia transform: --out requires --file")
75
+ return 2
76
+ }
77
+ return runTransformProject(prog, cwd, *tsconfigPath, plan)
78
+ }
79
+
80
+ absFile := *file
81
+ if !filepath.IsAbs(absFile) {
82
+ absFile = filepath.Join(cwd, absFile)
83
+ }
84
+ absFile = filepath.ToSlash(absFile)
85
+ target := prog.SourceFile(absFile)
86
+ if target == nil {
87
+ fmt.Fprintf(stderr, "ttsc-nestia transform: source file is not in program: %s\n", absFile)
88
+ return 2
89
+ }
90
+ rewrites, diagnostics := collectTypiaSourceRewrites(
91
+ prog,
92
+ cwd,
93
+ absFile,
94
+ readTypiaPluginOptions(cwd, *tsconfigPath),
95
+ )
96
+ if len(diagnostics) > 0 {
97
+ writeTypiaTransformDiagnostics(stderr, diagnostics, cwd)
98
+ return 3
99
+ }
100
+ coreRewriteMap, coreDiagnostics := collectNestiaCoreSourceRewriteMap(prog, plan, absFile)
101
+ if len(coreDiagnostics) > 0 {
102
+ writeTypiaTransformDiagnostics(stderr, coreDiagnostics, cwd)
103
+ return 3
104
+ }
105
+ rewrites = append(rewrites, coreRewriteMap[filepath.ToSlash(absFile)]...)
106
+ sdkRewriteMap, sdkDiagnostics := collectNestiaSDKSourceRewriteMap(prog, plan, absFile)
107
+ if len(sdkDiagnostics) > 0 {
108
+ writeTypiaTransformDiagnostics(stderr, sdkDiagnostics, cwd)
109
+ return 3
110
+ }
111
+ rewrites = append(rewrites, sdkRewriteMap[filepath.ToSlash(absFile)]...)
112
+ source, ok := sourceFileText(target)
113
+ if !ok {
114
+ fmt.Fprintf(stderr, "ttsc-nestia transform: source text is unavailable for %s\n", absFile)
115
+ return 3
116
+ }
117
+ source, err = applySourceRewrites(source, rewrites)
118
+ if err != nil {
119
+ fmt.Fprintf(stderr, "ttsc-nestia transform: source rewrite: %v\n", err)
120
+ return 3
121
+ }
122
+ source = cleanupTypeScriptTransformText(source)
123
+ if *out == "" {
124
+ if _, err := bytes.NewBufferString(source).WriteTo(stdout); err != nil {
125
+ fmt.Fprintf(stderr, "ttsc-nestia transform: write stdout: %v\n", err)
126
+ return 3
127
+ }
128
+ return 0
129
+ }
130
+ if dir := filepath.Dir(*out); dir != "" {
131
+ if err := os.MkdirAll(dir, 0o755); err != nil {
132
+ fmt.Fprintf(stderr, "ttsc-nestia transform: mkdir: %v\n", err)
133
+ return 3
134
+ }
135
+ }
136
+ if err := os.WriteFile(*out, []byte(source), 0o644); err != nil {
137
+ fmt.Fprintf(stderr, "ttsc-nestia transform: write %s: %v\n", *out, err)
138
+ return 3
139
+ }
140
+ return 0
141
+ }
142
+
143
+ func runTransformProject(prog *driver.Program, cwd string, tsconfigPath string, plan plugin.Plan) int {
144
+ rewrites, diags := collectTypiaSourceRewriteMap(
145
+ prog,
146
+ readTypiaPluginOptions(cwd, tsconfigPath),
147
+ )
148
+ coreRewriteMap, coreDiags := collectNestiaCoreSourceRewriteMap(prog, plan, "")
149
+ for file, entries := range coreRewriteMap {
150
+ rewrites[file] = append(rewrites[file], entries...)
151
+ }
152
+ diags = append(diags, coreDiags...)
153
+ sdkRewriteMap, sdkDiags := collectNestiaSDKSourceRewriteMap(prog, plan, "")
154
+ for file, entries := range sdkRewriteMap {
155
+ rewrites[file] = append(rewrites[file], entries...)
156
+ }
157
+ diags = append(diags, sdkDiags...)
158
+ output := transformProjectOutput{
159
+ Diagnostics: make([]transformCompilerDiagnostic, 0, len(diags)),
160
+ TypeScript: map[string]string{},
161
+ }
162
+ for _, diag := range diags {
163
+ output.Diagnostics = append(output.Diagnostics, transformDiagnosticToCompilerDiagnostic(diag))
164
+ }
165
+ for _, file := range prog.SourceFiles() {
166
+ filename := filepath.ToSlash(file.FileName())
167
+ source, ok := sourceFileText(file)
168
+ if !ok {
169
+ output.Diagnostics = append(
170
+ output.Diagnostics,
171
+ newTransformCompilerDiagnostic(filename, "nestia.transform", "source text is unavailable"),
172
+ )
173
+ continue
174
+ }
175
+ transformed, err := applySourceRewrites(source, rewrites[filename])
176
+ if err != nil {
177
+ output.Diagnostics = append(
178
+ output.Diagnostics,
179
+ newTransformCompilerDiagnostic(filename, "typia.transform", err.Error()),
180
+ )
181
+ continue
182
+ }
183
+ output.TypeScript[sourceFileKey(cwd, filename)] = cleanupTypeScriptTransformText(transformed)
184
+ }
185
+ if err := json.NewEncoder(stdout).Encode(output); err != nil {
186
+ fmt.Fprintf(stderr, "ttsc-nestia transform: encode output: %v\n", err)
187
+ return 3
188
+ }
189
+ if len(output.Diagnostics) > 0 {
190
+ return 3
191
+ }
192
+ return 0
193
+ }
194
+
195
+ type transformSourceRewrite struct {
196
+ start int
197
+ end int
198
+ replacement string
199
+ }
200
+
201
+ func collectTypiaSourceRewrites(
202
+ prog *driver.Program,
203
+ cwd string,
204
+ onlyFile string,
205
+ pluginOptions typiaadapter.PluginOptions,
206
+ ) ([]transformSourceRewrite, []typiaTransformDiagnostic) {
207
+ sites := collectNestiaTypiaCallSites(prog.SourceFiles(), prog.Checker)
208
+ rewrites := []transformSourceRewrite{}
209
+ diagnostics := []typiaTransformDiagnostic{}
210
+ for _, site := range sites {
211
+ if filepath.ToSlash(site.FilePath) != filepath.ToSlash(onlyFile) {
212
+ continue
213
+ }
214
+ if reason := typiaadapter.UnsupportedReason(site); reason != "" {
215
+ diagnostics = append(diagnostics, newTypiaTransformDiagnostic(site, reason))
216
+ continue
217
+ }
218
+ expr, handled, err := typiaadapter.EmitCallWithOptionsPreservingTypes(prog, site, pluginOptions)
219
+ if !handled {
220
+ diagnostics = append(diagnostics, newTypiaTransformDiagnostic(site, "method not covered"))
221
+ continue
222
+ }
223
+ if err != nil {
224
+ diagnostics = append(diagnostics, newTypiaTransformDiagnostic(site, err.Error()))
225
+ continue
226
+ }
227
+ expr = parenthesizeTypiaReplacement(site, expr)
228
+ node := site.Call.AsNode()
229
+ rewrites = append(rewrites, transformSourceRewrite{
230
+ start: node.Pos(),
231
+ end: node.End(),
232
+ replacement: expr,
233
+ })
234
+ _ = cwd
235
+ }
236
+ return rewrites, diagnostics
237
+ }
238
+
239
+ func collectTypiaSourceRewriteMap(
240
+ prog *driver.Program,
241
+ pluginOptions typiaadapter.PluginOptions,
242
+ ) (map[string][]transformSourceRewrite, []typiaTransformDiagnostic) {
243
+ sites := collectNestiaTypiaCallSites(prog.SourceFiles(), prog.Checker)
244
+ rewrites := map[string][]transformSourceRewrite{}
245
+ diagnostics := []typiaTransformDiagnostic{}
246
+ for _, site := range sites {
247
+ file := filepath.ToSlash(site.FilePath)
248
+ if reason := typiaadapter.UnsupportedReason(site); reason != "" {
249
+ diagnostics = append(diagnostics, newTypiaTransformDiagnostic(site, reason))
250
+ continue
251
+ }
252
+ expr, handled, err := typiaadapter.EmitCallWithOptionsPreservingTypes(prog, site, pluginOptions)
253
+ if !handled {
254
+ diagnostics = append(diagnostics, newTypiaTransformDiagnostic(site, "method not covered"))
255
+ continue
256
+ }
257
+ if err != nil {
258
+ diagnostics = append(diagnostics, newTypiaTransformDiagnostic(site, err.Error()))
259
+ continue
260
+ }
261
+ expr = parenthesizeTypiaReplacement(site, expr)
262
+ node := site.Call.AsNode()
263
+ rewrites[file] = append(rewrites[file], transformSourceRewrite{
264
+ start: node.Pos(),
265
+ end: node.End(),
266
+ replacement: expr,
267
+ })
268
+ }
269
+ return rewrites, diagnostics
270
+ }
271
+
272
+ func applySourceRewrites(source string, rewrites []transformSourceRewrite) (string, error) {
273
+ sort.SliceStable(rewrites, func(i, j int) bool {
274
+ return rewrites[i].start > rewrites[j].start
275
+ })
276
+ for i := 0; i < len(rewrites)-1; i++ {
277
+ if rewrites[i+1].end > rewrites[i].start {
278
+ return "", fmt.Errorf("overlapping rewrites: [%d,%d) vs [%d,%d)",
279
+ rewrites[i+1].start, rewrites[i+1].end, rewrites[i].start, rewrites[i].end)
280
+ }
281
+ }
282
+ output := source
283
+ for _, rewrite := range rewrites {
284
+ if rewrite.start < 0 || rewrite.end < rewrite.start || rewrite.end > len(output) {
285
+ return "", fmt.Errorf("invalid rewrite range [%d,%d)", rewrite.start, rewrite.end)
286
+ }
287
+ output = output[:rewrite.start] + rewrite.replacement + output[rewrite.end:]
288
+ }
289
+ return output, nil
290
+ }
291
+
292
+ func sourceFileText(target any) (string, bool) {
293
+ type sourceText interface {
294
+ Text() string
295
+ }
296
+ file, ok := target.(sourceText)
297
+ if !ok {
298
+ return "", false
299
+ }
300
+ return file.Text(), true
301
+ }
302
+
303
+ func cleanupTypeScriptTransformText(text string) string {
304
+ text = cleanupTransformedText(text)
305
+ text = normalizeParenthesizedTypeAnnotations(text)
306
+ text = regexp.MustCompile(`(?m)^import type \{([^{}\n]+)\} from`).ReplaceAllStringFunc(text, func(line string) string {
307
+ return regexp.MustCompile(`^import type \{\s*([^{}\n]+?)\s*\} from`).ReplaceAllString(line, "import type { $1 } from")
308
+ })
309
+ text = regexp.MustCompile(`(?m)(^import [^\n]+;\n)\n+(const |let |var |export )`).ReplaceAllString(text, "$1$2")
310
+ text = strings.ReplaceAll(text, "=(() =>", "= (() =>")
311
+ text = strings.ReplaceAll(text, ": (any) =>", ": any =>")
312
+ text = strings.ReplaceAll(text, ": (boolean) =>", ": boolean =>")
313
+ text = regexp.MustCompile(`input is \(([A-Za-z_$][A-Za-z0-9_$.]*)\)`).ReplaceAllString(text, "input is $1")
314
+ text = strings.ReplaceAll(text, "return (success ? ", "return success ? ")
315
+ text = strings.ReplaceAll(text, "}) as any;", "} as any;")
316
+ text = strings.ReplaceAll(text, "(() => {\n const ", "(() => { const ")
317
+ text = strings.ReplaceAll(text, "(() => {\n let ", "(() => { let ")
318
+ text = strings.ReplaceAll(text, "(() => {\n return ", "(() => { return ")
319
+ text = strings.ReplaceAll(text, ";\n const ", "; const ")
320
+ text = strings.ReplaceAll(text, ";\n let ", "; let ")
321
+ text = strings.ReplaceAll(text, ";\n return ", "; return ")
322
+ text = strings.ReplaceAll(text, "\n };\n})()", "\n}; })()")
323
+ text = strings.ReplaceAll(text, "\n });\n})()", "\n}); })()")
324
+ text = strings.ReplaceAll(text, "\n }); let ", "\n}); let ")
325
+ text = strings.ReplaceAll(text, ";\n})()", "; })()")
326
+ text = strings.ReplaceAll(text, "\n ", "\n ")
327
+ text = regexp.MustCompile(`\n\n([A-Za-z_$][A-Za-z0-9_$]*\([^;\n]*\);?)`).ReplaceAllString(text, "\n$1")
328
+ trimmed := strings.TrimRight(text, " \t\r\n")
329
+ if strings.HasSuffix(trimmed, ")") && !strings.HasSuffix(trimmed, ";") {
330
+ return trimmed + ";\n"
331
+ }
332
+ if text != "" && !strings.HasSuffix(text, "\n") {
333
+ return text + "\n"
334
+ }
335
+ return text
336
+ }
337
+
338
+ func normalizeParenthesizedTypeAnnotations(text string) string {
339
+ typeAtom := `([A-Za-z_$][A-Za-z0-9_$.]*(<[^()\n;{}]*>)?)`
340
+ text = regexp.MustCompile(`: \(`+typeAtom+`\)(\s*=>)`).ReplaceAllString(text, ": $1$3")
341
+ text = regexp.MustCompile(`\| \((null|undefined)\)`).ReplaceAllString(text, "| $1")
342
+ return text
343
+ }
344
+
345
+ func sourceFileKey(cwd string, file string) string {
346
+ rel, err := filepath.Rel(cwd, filepath.FromSlash(file))
347
+ if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
348
+ return filepath.ToSlash(file)
349
+ }
350
+ return filepath.ToSlash(rel)
351
+ }
352
+
353
+ func newTransformCompilerDiagnostic(
354
+ file string,
355
+ code string,
356
+ message string,
357
+ ) transformCompilerDiagnostic {
358
+ var ptr *string
359
+ if file != "" {
360
+ normalized := filepath.ToSlash(file)
361
+ ptr = &normalized
362
+ }
363
+ return transformCompilerDiagnostic{
364
+ File: ptr,
365
+ Category: "error",
366
+ Code: code,
367
+ MessageText: message,
368
+ }
369
+ }
370
+
371
+ func transformDiagnosticToCompilerDiagnostic(
372
+ diag typiaTransformDiagnostic,
373
+ ) transformCompilerDiagnostic {
374
+ var ptr *string
375
+ if diag.File != "" {
376
+ normalized := filepath.ToSlash(diag.File)
377
+ ptr = &normalized
378
+ }
379
+ return transformCompilerDiagnostic{
380
+ File: ptr,
381
+ Category: "error",
382
+ Code: diag.Code,
383
+ Line: diag.Line,
384
+ Character: diag.Column,
385
+ MessageText: diag.Message,
386
+ }
387
+ }
@@ -0,0 +1,326 @@
1
+ package main
2
+
3
+ import (
4
+ "path/filepath"
5
+ "strings"
6
+
7
+ shimast "github.com/microsoft/typescript-go/shim/ast"
8
+ shimchecker "github.com/microsoft/typescript-go/shim/checker"
9
+ typiaadapter "github.com/samchon/typia/packages/typia/native/adapter"
10
+ )
11
+
12
+ type nestiaTypiaImportRoot struct {
13
+ module string
14
+ }
15
+
16
+ func collectNestiaTypiaCallSites(files []*shimast.SourceFile, checker *shimchecker.Checker) []typiaadapter.CallSite {
17
+ sites := []typiaadapter.CallSite{}
18
+ for _, file := range files {
19
+ if file == nil || file.IsDeclarationFile {
20
+ continue
21
+ }
22
+ roots, fallback := nestiaTypiaImportRoots(file)
23
+ if len(roots) == 0 {
24
+ if fallback {
25
+ sites = append(sites, typiaadapter.CollectCallSites([]*shimast.SourceFile{file}, checker)...)
26
+ }
27
+ continue
28
+ }
29
+ file.ForEachChild(func(node *shimast.Node) bool {
30
+ visitNestiaTypiaCallSite(file, checker, roots, node, &sites)
31
+ return false
32
+ })
33
+ }
34
+ return sites
35
+ }
36
+
37
+ func visitNestiaTypiaCallSite(
38
+ file *shimast.SourceFile,
39
+ checker *shimchecker.Checker,
40
+ roots map[string]nestiaTypiaImportRoot,
41
+ node *shimast.Node,
42
+ sites *[]typiaadapter.CallSite,
43
+ ) {
44
+ if node == nil {
45
+ return
46
+ }
47
+ if node.Kind == shimast.KindCallExpression {
48
+ call := node.AsCallExpression()
49
+ if call != nil && nestiaTypiaCallLooksPossible(call, roots) {
50
+ if site, ok := tryNestiaTypiaCallSite(file, checker, node); ok {
51
+ *sites = append(*sites, site)
52
+ }
53
+ }
54
+ }
55
+ node.ForEachChild(func(child *shimast.Node) bool {
56
+ visitNestiaTypiaCallSite(file, checker, roots, child, sites)
57
+ return false
58
+ })
59
+ }
60
+
61
+ func nestiaTypiaImportRoots(file *shimast.SourceFile) (map[string]nestiaTypiaImportRoot, bool) {
62
+ roots := map[string]nestiaTypiaImportRoot{}
63
+ fallback := false
64
+ if file == nil || file.Statements == nil {
65
+ return roots, fallback
66
+ }
67
+ for _, stmt := range file.Statements.Nodes {
68
+ if stmt == nil || stmt.Kind != shimast.KindImportDeclaration {
69
+ continue
70
+ }
71
+ decl := stmt.AsImportDeclaration()
72
+ if decl == nil || decl.ImportClause == nil || decl.ModuleSpecifier == nil || decl.ModuleSpecifier.Kind != shimast.KindStringLiteral {
73
+ continue
74
+ }
75
+ moduleText := decl.ModuleSpecifier.Text()
76
+ module, ok := nestiaTypiaImportModule(moduleText)
77
+ if ok == false {
78
+ continue
79
+ }
80
+ clause := decl.ImportClause.AsImportClause()
81
+ if clause == nil || clause.PhaseModifier == shimast.KindTypeKeyword {
82
+ continue
83
+ }
84
+ if name := clause.Name(); name != nil {
85
+ roots[name.Text()] = nestiaTypiaImportRoot{module: module}
86
+ }
87
+ if clause.NamedBindings == nil {
88
+ continue
89
+ }
90
+ if clause.NamedBindings.Kind == shimast.KindNamespaceImport {
91
+ if name := clause.NamedBindings.Name(); name != nil {
92
+ roots[name.Text()] = nestiaTypiaImportRoot{module: module}
93
+ }
94
+ continue
95
+ }
96
+ if clause.NamedBindings.Kind != shimast.KindNamedImports {
97
+ continue
98
+ }
99
+ named := clause.NamedBindings.AsNamedImports()
100
+ if named == nil || named.Elements == nil {
101
+ continue
102
+ }
103
+ for _, elem := range named.Elements.Nodes {
104
+ if elem == nil {
105
+ continue
106
+ }
107
+ spec := elem.AsImportSpecifier()
108
+ if spec == nil || spec.IsTypeOnly {
109
+ continue
110
+ }
111
+ name := spec.Name()
112
+ if name == nil {
113
+ continue
114
+ }
115
+ imported := name.Text()
116
+ if spec.PropertyName != nil {
117
+ imported = spec.PropertyName.Text()
118
+ }
119
+ if module == "" {
120
+ if nestiaTypiaKnownModule(imported) == false {
121
+ continue
122
+ }
123
+ roots[name.Text()] = nestiaTypiaImportRoot{module: imported}
124
+ } else {
125
+ roots[name.Text()] = nestiaTypiaImportRoot{module: module}
126
+ }
127
+ }
128
+ }
129
+ return roots, fallback
130
+ }
131
+
132
+ func nestiaTypiaImportModule(specifier string) (string, bool) {
133
+ switch specifier {
134
+ case "typia":
135
+ return "", true
136
+ }
137
+ for _, prefix := range []string{"typia/lib/", "typia/src/"} {
138
+ if strings.HasPrefix(specifier, prefix) {
139
+ name := strings.TrimPrefix(specifier, prefix)
140
+ if strings.Contains(name, "/") || nestiaTypiaKnownModule(name) == false {
141
+ return "", false
142
+ }
143
+ return name, true
144
+ }
145
+ }
146
+ return "", false
147
+ }
148
+
149
+ func nestiaTypiaKnownModule(name string) bool {
150
+ switch name {
151
+ case "functional", "http", "json", "llm", "misc", "module", "notations", "protobuf", "reflect":
152
+ return true
153
+ default:
154
+ return false
155
+ }
156
+ }
157
+
158
+ func nestiaTypiaCallLooksPossible(call *shimast.CallExpression, roots map[string]nestiaTypiaImportRoot) bool {
159
+ root, parts, ok := nestiaTypiaCallSegments(call)
160
+ if ok == false {
161
+ return false
162
+ }
163
+ info, ok := roots[root]
164
+ if ok == false || len(parts) == 0 {
165
+ return false
166
+ }
167
+ if info.module != "" {
168
+ return true
169
+ }
170
+ if len(parts) == 1 {
171
+ return true
172
+ }
173
+ return nestiaTypiaKnownModule(parts[0])
174
+ }
175
+
176
+ func nestiaTypiaCallSegments(call *shimast.CallExpression) (string, []string, bool) {
177
+ if call == nil {
178
+ return "", nil, false
179
+ }
180
+ expression := call.Expression
181
+ parts := []string{}
182
+ for expression != nil && expression.Kind == shimast.KindPropertyAccessExpression {
183
+ property := expression.AsPropertyAccessExpression()
184
+ if property == nil {
185
+ return "", nil, false
186
+ }
187
+ name := property.Name()
188
+ if name == nil || name.Kind != shimast.KindIdentifier {
189
+ return "", nil, false
190
+ }
191
+ id := name.AsIdentifier()
192
+ if id == nil || id.Text == "" {
193
+ return "", nil, false
194
+ }
195
+ parts = append([]string{id.Text}, parts...)
196
+ expression = property.Expression
197
+ }
198
+ if expression == nil || expression.Kind != shimast.KindIdentifier {
199
+ return "", nil, false
200
+ }
201
+ root := expression.AsIdentifier()
202
+ if root == nil || root.Text == "" {
203
+ return "", nil, false
204
+ }
205
+ return root.Text, parts, true
206
+ }
207
+
208
+ func tryNestiaTypiaCallSite(file *shimast.SourceFile, checker *shimchecker.Checker, node *shimast.Node) (typiaadapter.CallSite, bool) {
209
+ if checker == nil {
210
+ return typiaadapter.CallSite{}, false
211
+ }
212
+ call := node.AsCallExpression()
213
+ if call == nil {
214
+ return typiaadapter.CallSite{}, false
215
+ }
216
+ signature := checker.GetResolvedSignature(node)
217
+ if signature == nil {
218
+ return typiaadapter.CallSite{}, false
219
+ }
220
+ declaration := signature.Declaration()
221
+ if declaration == nil {
222
+ return typiaadapter.CallSite{}, false
223
+ }
224
+ sourceFile := shimast.GetSourceFileOfNode(declaration)
225
+ if sourceFile == nil {
226
+ return typiaadapter.CallSite{}, false
227
+ }
228
+ module, ok := nestiaTypiaMatchModule(sourceFile.FileName())
229
+ if !ok {
230
+ return typiaadapter.CallSite{}, false
231
+ }
232
+ method := nestiaTypiaCallSiteMethodName(checker, declaration, call)
233
+ if method == "" {
234
+ return typiaadapter.CallSite{}, false
235
+ }
236
+ root, namespaces, ok := nestiaTypiaExtractRootAndNamespaces(call, method)
237
+ if !ok {
238
+ return typiaadapter.CallSite{}, false
239
+ }
240
+ return typiaadapter.CallSite{
241
+ File: file,
242
+ FilePath: file.FileName(),
243
+ Module: module,
244
+ Method: method,
245
+ Call: call,
246
+ RootName: root,
247
+ Namespaces: namespaces,
248
+ }, true
249
+ }
250
+
251
+ func nestiaTypiaMatchModule(location string) (string, bool) {
252
+ location = filepath.ToSlash(location)
253
+ for _, suffix := range []string{".d.ts", ".ts"} {
254
+ for _, middle := range []string{"typia/lib/", "typia/src/", "packages/typia/src/"} {
255
+ index := strings.LastIndex(location, middle)
256
+ if index < 0 {
257
+ continue
258
+ }
259
+ name := location[index+len(middle):]
260
+ if !strings.HasSuffix(name, suffix) {
261
+ continue
262
+ }
263
+ name = strings.TrimSuffix(name, suffix)
264
+ if strings.Contains(name, "/") {
265
+ continue
266
+ }
267
+ return name, true
268
+ }
269
+ }
270
+ return "", false
271
+ }
272
+
273
+ func nestiaTypiaCallSiteMethodName(checker *shimchecker.Checker, declaration *shimast.Node, call *shimast.CallExpression) string {
274
+ if name := declaration.Name(); name != nil {
275
+ if symbol := checker.GetSymbolAtLocation(name); symbol != nil && symbol.Name != "" {
276
+ return symbol.Name
277
+ }
278
+ if name.Kind == shimast.KindIdentifier {
279
+ if id := name.AsIdentifier(); id != nil {
280
+ return id.Text
281
+ }
282
+ }
283
+ }
284
+ if call.Expression != nil && call.Expression.Kind == shimast.KindPropertyAccessExpression {
285
+ if property := call.Expression.AsPropertyAccessExpression(); property != nil {
286
+ if name := property.Name(); name != nil && name.Kind == shimast.KindIdentifier {
287
+ if id := name.AsIdentifier(); id != nil {
288
+ return id.Text
289
+ }
290
+ }
291
+ }
292
+ }
293
+ return ""
294
+ }
295
+
296
+ func nestiaTypiaExtractRootAndNamespaces(call *shimast.CallExpression, method string) (string, []string, bool) {
297
+ expression := call.Expression
298
+ segments := []string{}
299
+ for expression != nil && expression.Kind == shimast.KindPropertyAccessExpression {
300
+ property := expression.AsPropertyAccessExpression()
301
+ if property == nil {
302
+ break
303
+ }
304
+ name := property.Name()
305
+ if name == nil || name.Kind != shimast.KindIdentifier {
306
+ return "", nil, false
307
+ }
308
+ id := name.AsIdentifier()
309
+ if id == nil || id.Text == "" {
310
+ return "", nil, false
311
+ }
312
+ segments = append([]string{id.Text}, segments...)
313
+ expression = property.Expression
314
+ }
315
+ if expression == nil || expression.Kind != shimast.KindIdentifier {
316
+ return "", nil, false
317
+ }
318
+ root := expression.AsIdentifier()
319
+ if root == nil || root.Text == "" || len(segments) == 0 {
320
+ return "", nil, false
321
+ }
322
+ if segments[len(segments)-1] != method {
323
+ return "", nil, false
324
+ }
325
+ return root.Text, segments[:len(segments)-1], true
326
+ }