@nestia/core 12.0.0-rc.2 → 12.0.0-rc.3

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 (80) hide show
  1. package/LICENSE +21 -21
  2. package/MIGRATION.md +138 -169
  3. package/lib/decorators/NoTransformConfigurationError.js +5 -5
  4. package/lib/decorators/NoTransformConfigurationError.js.map +1 -1
  5. package/native/cmd/ttsc-nestia/main.go +11 -11
  6. package/native/go.mod +32 -32
  7. package/native/go.sum +54 -54
  8. package/native/plugin/plan.go +102 -102
  9. package/native/transform/ast.go +32 -32
  10. package/native/transform/build.go +388 -380
  11. package/native/transform/cleanup.go +408 -408
  12. package/native/transform/contributor.go +97 -97
  13. package/native/transform/core_querify.go +231 -231
  14. package/native/transform/core_transform.go +1996 -1996
  15. package/native/transform/core_websocket.go +115 -115
  16. package/native/transform/exports.go +13 -13
  17. package/native/transform/mcp_transform.go +414 -414
  18. package/native/transform/node_transform.go +357 -357
  19. package/native/transform/path_rewrite.go +285 -285
  20. package/native/transform/printer.go +244 -244
  21. package/native/transform/rewrite.go +668 -668
  22. package/native/transform/run.go +73 -73
  23. package/native/transform/transform.go +336 -336
  24. package/native/transform/typia_fast.go +375 -352
  25. package/native/transform/typia_replacement.go +24 -24
  26. package/native/transform.cjs +43 -43
  27. package/package.json +9 -9
  28. package/src/adaptors/McpAdaptor.ts +276 -276
  29. package/src/adaptors/WebSocketAdaptor.ts +429 -429
  30. package/src/decorators/DynamicModule.ts +44 -44
  31. package/src/decorators/EncryptedBody.ts +97 -97
  32. package/src/decorators/EncryptedController.ts +40 -40
  33. package/src/decorators/EncryptedModule.ts +98 -98
  34. package/src/decorators/EncryptedRoute.ts +213 -213
  35. package/src/decorators/HumanRoute.ts +21 -21
  36. package/src/decorators/McpRoute.ts +154 -154
  37. package/src/decorators/NoTransformConfigurationError.ts +40 -40
  38. package/src/decorators/PlainBody.ts +76 -76
  39. package/src/decorators/SwaggerCustomizer.ts +97 -97
  40. package/src/decorators/SwaggerExample.ts +180 -180
  41. package/src/decorators/TypedBody.ts +57 -57
  42. package/src/decorators/TypedException.ts +147 -147
  43. package/src/decorators/TypedFormData.ts +187 -187
  44. package/src/decorators/TypedHeaders.ts +66 -66
  45. package/src/decorators/TypedParam.ts +77 -77
  46. package/src/decorators/TypedQuery.ts +234 -234
  47. package/src/decorators/TypedRoute.ts +198 -198
  48. package/src/decorators/WebSocketRoute.ts +242 -242
  49. package/src/decorators/doNotThrowTransformError.ts +5 -5
  50. package/src/decorators/internal/EncryptedConstant.ts +2 -2
  51. package/src/decorators/internal/IMcpRouteReflect.ts +40 -40
  52. package/src/decorators/internal/IWebSocketRouteReflect.ts +23 -23
  53. package/src/decorators/internal/get_path_and_querify.ts +94 -94
  54. package/src/decorators/internal/get_path_and_stringify.ts +110 -110
  55. package/src/decorators/internal/get_text_body.ts +16 -16
  56. package/src/decorators/internal/headers_to_object.ts +11 -11
  57. package/src/decorators/internal/is_request_body_undefined.ts +12 -12
  58. package/src/decorators/internal/load_controller.ts +94 -94
  59. package/src/decorators/internal/route_error.ts +43 -43
  60. package/src/decorators/internal/validate_request_body.ts +64 -64
  61. package/src/decorators/internal/validate_request_form_data.ts +67 -67
  62. package/src/decorators/internal/validate_request_headers.ts +76 -76
  63. package/src/decorators/internal/validate_request_query.ts +83 -83
  64. package/src/index.ts +5 -5
  65. package/src/module.ts +25 -25
  66. package/src/options/IRequestBodyValidator.ts +20 -20
  67. package/src/options/IRequestFormDataProps.ts +27 -27
  68. package/src/options/IRequestHeadersValidator.ts +22 -22
  69. package/src/options/IRequestQueryValidator.ts +20 -20
  70. package/src/options/IResponseBodyQuerifier.ts +25 -25
  71. package/src/options/IResponseBodyStringifier.ts +30 -30
  72. package/src/transform.ts +26 -26
  73. package/src/typings/Creator.ts +3 -3
  74. package/src/typings/get-function-location.d.ts +7 -7
  75. package/src/utils/ArrayUtil.ts +7 -7
  76. package/src/utils/ExceptionManager.ts +115 -115
  77. package/src/utils/Singleton.ts +16 -16
  78. package/src/utils/SourceFinder.ts +54 -54
  79. package/src/utils/VersioningStrategy.ts +27 -27
  80. package/README.md +0 -93
@@ -1,352 +1,375 @@
1
- package transform
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
- shimprinter "github.com/microsoft/typescript-go/shim/printer"
10
- "github.com/samchon/ttsc/packages/ttsc/driver"
11
- typiaadapter "github.com/samchon/typia/packages/typia/native/adapter"
12
- nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
13
- nativetransform "github.com/samchon/typia/packages/typia/native/transform"
14
- )
15
-
16
- // nestiaTypiaNodeTransform wraps typia's per-file AST transformer as a ttsc
17
- // PluginTransform. typia owns its call-site detection and (in ec mode) injects
18
- // the namespace imports tsgo's module-transform aliases, so @nestia/core simply
19
- // runs it alongside its own decorator transform inside the same emit pipeline.
20
- // A recovered typia transform error is reported as a diagnostic.
21
- func nestiaTypiaNodeTransform(
22
- prog *driver.Program,
23
- pluginOptions typiaadapter.PluginOptions,
24
- addDiagnostic func(Diagnostic),
25
- ) driver.PluginTransform {
26
- transformOptions := pluginOptions.TransformOptions()
27
- extras := nativecontext.ITypiaContext_Extras{
28
- AddDiagnostic: func(diag *shimast.Diagnostic) int {
29
- addDiagnostic(Diagnostic{Code: "typia.transform", Message: "typia transform error"})
30
- return 1
31
- },
32
- }
33
- return func(ec *shimprinter.EmitContext, sf *shimast.SourceFile) *shimast.SourceFile {
34
- return nativetransform.Transform(prog, &transformOptions, extras, ec)(sf)
35
- }
36
- }
37
-
38
- type nestiaTypiaImportRoot struct {
39
- module string
40
- }
41
-
42
- func collectNestiaTypiaCallSites(files []*shimast.SourceFile, checker *shimchecker.Checker) []typiaadapter.CallSite {
43
- sites := []typiaadapter.CallSite{}
44
- for _, file := range files {
45
- if file == nil || file.IsDeclarationFile {
46
- continue
47
- }
48
- roots, fallback := nestiaTypiaImportRoots(file)
49
- if len(roots) == 0 {
50
- if fallback {
51
- sites = append(sites, typiaadapter.CollectCallSites([]*shimast.SourceFile{file}, checker)...)
52
- }
53
- continue
54
- }
55
- file.ForEachChild(func(node *shimast.Node) bool {
56
- visitNestiaTypiaCallSite(file, checker, roots, node, &sites)
57
- return false
58
- })
59
- }
60
- return sites
61
- }
62
-
63
- func visitNestiaTypiaCallSite(
64
- file *shimast.SourceFile,
65
- checker *shimchecker.Checker,
66
- roots map[string]nestiaTypiaImportRoot,
67
- node *shimast.Node,
68
- sites *[]typiaadapter.CallSite,
69
- ) {
70
- if node == nil {
71
- return
72
- }
73
- if node.Kind == shimast.KindCallExpression {
74
- call := node.AsCallExpression()
75
- if call != nil && nestiaTypiaCallLooksPossible(call, roots) {
76
- if site, ok := tryNestiaTypiaCallSite(file, checker, node); ok {
77
- *sites = append(*sites, site)
78
- }
79
- }
80
- }
81
- node.ForEachChild(func(child *shimast.Node) bool {
82
- visitNestiaTypiaCallSite(file, checker, roots, child, sites)
83
- return false
84
- })
85
- }
86
-
87
- func nestiaTypiaImportRoots(file *shimast.SourceFile) (map[string]nestiaTypiaImportRoot, bool) {
88
- roots := map[string]nestiaTypiaImportRoot{}
89
- fallback := false
90
- if file == nil || file.Statements == nil {
91
- return roots, fallback
92
- }
93
- for _, stmt := range file.Statements.Nodes {
94
- if stmt == nil || stmt.Kind != shimast.KindImportDeclaration {
95
- continue
96
- }
97
- decl := stmt.AsImportDeclaration()
98
- if decl == nil || decl.ImportClause == nil || decl.ModuleSpecifier == nil || decl.ModuleSpecifier.Kind != shimast.KindStringLiteral {
99
- continue
100
- }
101
- moduleText := decl.ModuleSpecifier.Text()
102
- module, ok := nestiaTypiaImportModule(moduleText)
103
- if ok == false {
104
- continue
105
- }
106
- clause := decl.ImportClause.AsImportClause()
107
- if clause == nil || clause.PhaseModifier == shimast.KindTypeKeyword {
108
- continue
109
- }
110
- if name := clause.Name(); name != nil {
111
- roots[name.Text()] = nestiaTypiaImportRoot{module: module}
112
- }
113
- if clause.NamedBindings == nil {
114
- continue
115
- }
116
- if clause.NamedBindings.Kind == shimast.KindNamespaceImport {
117
- if name := clause.NamedBindings.Name(); name != nil {
118
- roots[name.Text()] = nestiaTypiaImportRoot{module: module}
119
- }
120
- continue
121
- }
122
- if clause.NamedBindings.Kind != shimast.KindNamedImports {
123
- continue
124
- }
125
- named := clause.NamedBindings.AsNamedImports()
126
- if named == nil || named.Elements == nil {
127
- continue
128
- }
129
- for _, elem := range named.Elements.Nodes {
130
- if elem == nil {
131
- continue
132
- }
133
- spec := elem.AsImportSpecifier()
134
- if spec == nil || spec.IsTypeOnly {
135
- continue
136
- }
137
- name := spec.Name()
138
- if name == nil {
139
- continue
140
- }
141
- imported := name.Text()
142
- if spec.PropertyName != nil {
143
- imported = spec.PropertyName.Text()
144
- }
145
- if module == "" {
146
- if nestiaTypiaKnownModule(imported) == false {
147
- continue
148
- }
149
- roots[name.Text()] = nestiaTypiaImportRoot{module: imported}
150
- } else {
151
- roots[name.Text()] = nestiaTypiaImportRoot{module: module}
152
- }
153
- }
154
- }
155
- return roots, fallback
156
- }
157
-
158
- func nestiaTypiaImportModule(specifier string) (string, bool) {
159
- switch specifier {
160
- case "typia":
161
- return "", true
162
- }
163
- for _, prefix := range []string{"typia/lib/", "typia/src/"} {
164
- if strings.HasPrefix(specifier, prefix) {
165
- name := strings.TrimPrefix(specifier, prefix)
166
- if strings.Contains(name, "/") || nestiaTypiaKnownModule(name) == false {
167
- return "", false
168
- }
169
- return name, true
170
- }
171
- }
172
- return "", false
173
- }
174
-
175
- func nestiaTypiaKnownModule(name string) bool {
176
- switch name {
177
- case "functional", "http", "json", "llm", "misc", "module", "notations", "protobuf", "reflect":
178
- return true
179
- default:
180
- return false
181
- }
182
- }
183
-
184
- func nestiaTypiaCallLooksPossible(call *shimast.CallExpression, roots map[string]nestiaTypiaImportRoot) bool {
185
- root, parts, ok := nestiaTypiaCallSegments(call)
186
- if ok == false {
187
- return false
188
- }
189
- info, ok := roots[root]
190
- if ok == false || len(parts) == 0 {
191
- return false
192
- }
193
- if info.module != "" {
194
- return true
195
- }
196
- if len(parts) == 1 {
197
- return true
198
- }
199
- return nestiaTypiaKnownModule(parts[0])
200
- }
201
-
202
- func nestiaTypiaCallSegments(call *shimast.CallExpression) (string, []string, bool) {
203
- if call == nil {
204
- return "", nil, false
205
- }
206
- expression := call.Expression
207
- parts := []string{}
208
- for expression != nil && expression.Kind == shimast.KindPropertyAccessExpression {
209
- property := expression.AsPropertyAccessExpression()
210
- if property == nil {
211
- return "", nil, false
212
- }
213
- name := property.Name()
214
- if name == nil || name.Kind != shimast.KindIdentifier {
215
- return "", nil, false
216
- }
217
- id := name.AsIdentifier()
218
- if id == nil || id.Text == "" {
219
- return "", nil, false
220
- }
221
- parts = append([]string{id.Text}, parts...)
222
- expression = property.Expression
223
- }
224
- if expression == nil || expression.Kind != shimast.KindIdentifier {
225
- return "", nil, false
226
- }
227
- root := expression.AsIdentifier()
228
- if root == nil || root.Text == "" {
229
- return "", nil, false
230
- }
231
- return root.Text, parts, true
232
- }
233
-
234
- func tryNestiaTypiaCallSite(file *shimast.SourceFile, checker *shimchecker.Checker, node *shimast.Node) (typiaadapter.CallSite, bool) {
235
- if checker == nil {
236
- return typiaadapter.CallSite{}, false
237
- }
238
- call := node.AsCallExpression()
239
- if call == nil {
240
- return typiaadapter.CallSite{}, false
241
- }
242
- signature := checker.GetResolvedSignature(node)
243
- if signature == nil {
244
- return typiaadapter.CallSite{}, false
245
- }
246
- declaration := signature.Declaration()
247
- if declaration == nil {
248
- return typiaadapter.CallSite{}, false
249
- }
250
- sourceFile := shimast.GetSourceFileOfNode(declaration)
251
- if sourceFile == nil {
252
- return typiaadapter.CallSite{}, false
253
- }
254
- module, ok := nestiaTypiaMatchModule(sourceFile.FileName())
255
- if !ok {
256
- return typiaadapter.CallSite{}, false
257
- }
258
- method := nestiaTypiaCallSiteMethodName(checker, declaration, call)
259
- if method == "" {
260
- return typiaadapter.CallSite{}, false
261
- }
262
- root, namespaces, ok := nestiaTypiaExtractRootAndNamespaces(call, method)
263
- if !ok {
264
- return typiaadapter.CallSite{}, false
265
- }
266
- return typiaadapter.CallSite{
267
- File: file,
268
- FilePath: file.FileName(),
269
- Module: module,
270
- Method: method,
271
- Call: call,
272
- RootName: root,
273
- Namespaces: namespaces,
274
- }, true
275
- }
276
-
277
- func nestiaTypiaMatchModule(location string) (string, bool) {
278
- location = filepath.ToSlash(location)
279
- for _, suffix := range []string{".d.ts", ".ts"} {
280
- for _, middle := range []string{"typia/lib/", "typia/src/", "packages/typia/src/"} {
281
- index := strings.LastIndex(location, middle)
282
- if index < 0 {
283
- continue
284
- }
285
- name := location[index+len(middle):]
286
- if !strings.HasSuffix(name, suffix) {
287
- continue
288
- }
289
- name = strings.TrimSuffix(name, suffix)
290
- if strings.Contains(name, "/") {
291
- continue
292
- }
293
- return name, true
294
- }
295
- }
296
- return "", false
297
- }
298
-
299
- func nestiaTypiaCallSiteMethodName(checker *shimchecker.Checker, declaration *shimast.Node, call *shimast.CallExpression) string {
300
- if name := declaration.Name(); name != nil {
301
- if symbol := checker.GetSymbolAtLocation(name); symbol != nil && symbol.Name != "" {
302
- return symbol.Name
303
- }
304
- if name.Kind == shimast.KindIdentifier {
305
- if id := name.AsIdentifier(); id != nil {
306
- return id.Text
307
- }
308
- }
309
- }
310
- if call.Expression != nil && call.Expression.Kind == shimast.KindPropertyAccessExpression {
311
- if property := call.Expression.AsPropertyAccessExpression(); property != nil {
312
- if name := property.Name(); name != nil && name.Kind == shimast.KindIdentifier {
313
- if id := name.AsIdentifier(); id != nil {
314
- return id.Text
315
- }
316
- }
317
- }
318
- }
319
- return ""
320
- }
321
-
322
- func nestiaTypiaExtractRootAndNamespaces(call *shimast.CallExpression, method string) (string, []string, bool) {
323
- expression := call.Expression
324
- segments := []string{}
325
- for expression != nil && expression.Kind == shimast.KindPropertyAccessExpression {
326
- property := expression.AsPropertyAccessExpression()
327
- if property == nil {
328
- break
329
- }
330
- name := property.Name()
331
- if name == nil || name.Kind != shimast.KindIdentifier {
332
- return "", nil, false
333
- }
334
- id := name.AsIdentifier()
335
- if id == nil || id.Text == "" {
336
- return "", nil, false
337
- }
338
- segments = append([]string{id.Text}, segments...)
339
- expression = property.Expression
340
- }
341
- if expression == nil || expression.Kind != shimast.KindIdentifier {
342
- return "", nil, false
343
- }
344
- root := expression.AsIdentifier()
345
- if root == nil || root.Text == "" || len(segments) == 0 {
346
- return "", nil, false
347
- }
348
- if segments[len(segments)-1] != method {
349
- return "", nil, false
350
- }
351
- return root.Text, segments[:len(segments)-1], true
352
- }
1
+ package transform
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
+ shimprinter "github.com/microsoft/typescript-go/shim/printer"
10
+ shimscanner "github.com/microsoft/typescript-go/shim/scanner"
11
+ "github.com/samchon/ttsc/packages/ttsc/driver"
12
+ typiaadapter "github.com/samchon/typia/packages/typia/native/adapter"
13
+ nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
14
+ nativetransform "github.com/samchon/typia/packages/typia/native/transform"
15
+ )
16
+
17
+ // nestiaTypiaNodeTransform wraps typia's per-file AST transformer as a ttsc
18
+ // PluginTransform. typia owns its call-site detection and (in ec mode) injects
19
+ // the namespace imports tsgo's module-transform aliases, so @nestia/core simply
20
+ // runs it alongside its own decorator transform inside the same emit pipeline.
21
+ // A recovered typia transform error is reported as a diagnostic.
22
+ func nestiaTypiaNodeTransform(
23
+ prog *driver.Program,
24
+ pluginOptions typiaadapter.PluginOptions,
25
+ addDiagnostic func(Diagnostic),
26
+ ) driver.PluginTransform {
27
+ transformOptions := pluginOptions.TransformOptions()
28
+ extras := nativecontext.ITypiaContext_Extras{
29
+ AddDiagnostic: func(diag *nativecontext.ITypiaDiagnostic) int {
30
+ addDiagnostic(nestiaTypiaDiagnosticFrom(diag))
31
+ return 1
32
+ },
33
+ }
34
+ return func(ec *shimprinter.EmitContext, sf *shimast.SourceFile) *shimast.SourceFile {
35
+ return nativetransform.Transform(prog, &transformOptions, extras, ec)(sf)
36
+ }
37
+ }
38
+
39
+ func nestiaTypiaDiagnosticFrom(diag *nativecontext.ITypiaDiagnostic) Diagnostic {
40
+ out := Diagnostic{Code: "typia.transform", Message: "typia transform error"}
41
+ if diag == nil {
42
+ return out
43
+ }
44
+ if diag.Code != "" {
45
+ out.Code = diag.Code
46
+ }
47
+ if diag.Message != "" {
48
+ out.Message = diag.Message
49
+ }
50
+ if diag.File != nil {
51
+ out.File = diag.File.FileName()
52
+ if diag.Start != nil && *diag.Start >= 0 {
53
+ line, column := shimscanner.GetECMALineAndByteOffsetOfPosition(diag.File, *diag.Start)
54
+ out.Line = line + 1
55
+ out.Column = column + 1
56
+ }
57
+ }
58
+ return out
59
+ }
60
+
61
+ type nestiaTypiaImportRoot struct {
62
+ module string
63
+ }
64
+
65
+ func collectNestiaTypiaCallSites(files []*shimast.SourceFile, checker *shimchecker.Checker) []typiaadapter.CallSite {
66
+ sites := []typiaadapter.CallSite{}
67
+ for _, file := range files {
68
+ if file == nil || file.IsDeclarationFile {
69
+ continue
70
+ }
71
+ roots, fallback := nestiaTypiaImportRoots(file)
72
+ if len(roots) == 0 {
73
+ if fallback {
74
+ sites = append(sites, typiaadapter.CollectCallSites([]*shimast.SourceFile{file}, checker)...)
75
+ }
76
+ continue
77
+ }
78
+ file.ForEachChild(func(node *shimast.Node) bool {
79
+ visitNestiaTypiaCallSite(file, checker, roots, node, &sites)
80
+ return false
81
+ })
82
+ }
83
+ return sites
84
+ }
85
+
86
+ func visitNestiaTypiaCallSite(
87
+ file *shimast.SourceFile,
88
+ checker *shimchecker.Checker,
89
+ roots map[string]nestiaTypiaImportRoot,
90
+ node *shimast.Node,
91
+ sites *[]typiaadapter.CallSite,
92
+ ) {
93
+ if node == nil {
94
+ return
95
+ }
96
+ if node.Kind == shimast.KindCallExpression {
97
+ call := node.AsCallExpression()
98
+ if call != nil && nestiaTypiaCallLooksPossible(call, roots) {
99
+ if site, ok := tryNestiaTypiaCallSite(file, checker, node); ok {
100
+ *sites = append(*sites, site)
101
+ }
102
+ }
103
+ }
104
+ node.ForEachChild(func(child *shimast.Node) bool {
105
+ visitNestiaTypiaCallSite(file, checker, roots, child, sites)
106
+ return false
107
+ })
108
+ }
109
+
110
+ func nestiaTypiaImportRoots(file *shimast.SourceFile) (map[string]nestiaTypiaImportRoot, bool) {
111
+ roots := map[string]nestiaTypiaImportRoot{}
112
+ fallback := false
113
+ if file == nil || file.Statements == nil {
114
+ return roots, fallback
115
+ }
116
+ for _, stmt := range file.Statements.Nodes {
117
+ if stmt == nil || stmt.Kind != shimast.KindImportDeclaration {
118
+ continue
119
+ }
120
+ decl := stmt.AsImportDeclaration()
121
+ if decl == nil || decl.ImportClause == nil || decl.ModuleSpecifier == nil || decl.ModuleSpecifier.Kind != shimast.KindStringLiteral {
122
+ continue
123
+ }
124
+ moduleText := decl.ModuleSpecifier.Text()
125
+ module, ok := nestiaTypiaImportModule(moduleText)
126
+ if ok == false {
127
+ continue
128
+ }
129
+ clause := decl.ImportClause.AsImportClause()
130
+ if clause == nil || clause.PhaseModifier == shimast.KindTypeKeyword {
131
+ continue
132
+ }
133
+ if name := clause.Name(); name != nil {
134
+ roots[name.Text()] = nestiaTypiaImportRoot{module: module}
135
+ }
136
+ if clause.NamedBindings == nil {
137
+ continue
138
+ }
139
+ if clause.NamedBindings.Kind == shimast.KindNamespaceImport {
140
+ if name := clause.NamedBindings.Name(); name != nil {
141
+ roots[name.Text()] = nestiaTypiaImportRoot{module: module}
142
+ }
143
+ continue
144
+ }
145
+ if clause.NamedBindings.Kind != shimast.KindNamedImports {
146
+ continue
147
+ }
148
+ named := clause.NamedBindings.AsNamedImports()
149
+ if named == nil || named.Elements == nil {
150
+ continue
151
+ }
152
+ for _, elem := range named.Elements.Nodes {
153
+ if elem == nil {
154
+ continue
155
+ }
156
+ spec := elem.AsImportSpecifier()
157
+ if spec == nil || spec.IsTypeOnly {
158
+ continue
159
+ }
160
+ name := spec.Name()
161
+ if name == nil {
162
+ continue
163
+ }
164
+ imported := name.Text()
165
+ if spec.PropertyName != nil {
166
+ imported = spec.PropertyName.Text()
167
+ }
168
+ if module == "" {
169
+ if nestiaTypiaKnownModule(imported) == false {
170
+ continue
171
+ }
172
+ roots[name.Text()] = nestiaTypiaImportRoot{module: imported}
173
+ } else {
174
+ roots[name.Text()] = nestiaTypiaImportRoot{module: module}
175
+ }
176
+ }
177
+ }
178
+ return roots, fallback
179
+ }
180
+
181
+ func nestiaTypiaImportModule(specifier string) (string, bool) {
182
+ switch specifier {
183
+ case "typia":
184
+ return "", true
185
+ }
186
+ for _, prefix := range []string{"typia/lib/", "typia/src/"} {
187
+ if strings.HasPrefix(specifier, prefix) {
188
+ name := strings.TrimPrefix(specifier, prefix)
189
+ if strings.Contains(name, "/") || nestiaTypiaKnownModule(name) == false {
190
+ return "", false
191
+ }
192
+ return name, true
193
+ }
194
+ }
195
+ return "", false
196
+ }
197
+
198
+ func nestiaTypiaKnownModule(name string) bool {
199
+ switch name {
200
+ case "functional", "http", "json", "llm", "misc", "module", "notations", "protobuf", "reflect":
201
+ return true
202
+ default:
203
+ return false
204
+ }
205
+ }
206
+
207
+ func nestiaTypiaCallLooksPossible(call *shimast.CallExpression, roots map[string]nestiaTypiaImportRoot) bool {
208
+ root, parts, ok := nestiaTypiaCallSegments(call)
209
+ if ok == false {
210
+ return false
211
+ }
212
+ info, ok := roots[root]
213
+ if ok == false || len(parts) == 0 {
214
+ return false
215
+ }
216
+ if info.module != "" {
217
+ return true
218
+ }
219
+ if len(parts) == 1 {
220
+ return true
221
+ }
222
+ return nestiaTypiaKnownModule(parts[0])
223
+ }
224
+
225
+ func nestiaTypiaCallSegments(call *shimast.CallExpression) (string, []string, bool) {
226
+ if call == nil {
227
+ return "", nil, false
228
+ }
229
+ expression := call.Expression
230
+ parts := []string{}
231
+ for expression != nil && expression.Kind == shimast.KindPropertyAccessExpression {
232
+ property := expression.AsPropertyAccessExpression()
233
+ if property == nil {
234
+ return "", nil, false
235
+ }
236
+ name := property.Name()
237
+ if name == nil || name.Kind != shimast.KindIdentifier {
238
+ return "", nil, false
239
+ }
240
+ id := name.AsIdentifier()
241
+ if id == nil || id.Text == "" {
242
+ return "", nil, false
243
+ }
244
+ parts = append([]string{id.Text}, parts...)
245
+ expression = property.Expression
246
+ }
247
+ if expression == nil || expression.Kind != shimast.KindIdentifier {
248
+ return "", nil, false
249
+ }
250
+ root := expression.AsIdentifier()
251
+ if root == nil || root.Text == "" {
252
+ return "", nil, false
253
+ }
254
+ return root.Text, parts, true
255
+ }
256
+
257
+ func tryNestiaTypiaCallSite(file *shimast.SourceFile, checker *shimchecker.Checker, node *shimast.Node) (typiaadapter.CallSite, bool) {
258
+ if checker == nil {
259
+ return typiaadapter.CallSite{}, false
260
+ }
261
+ call := node.AsCallExpression()
262
+ if call == nil {
263
+ return typiaadapter.CallSite{}, false
264
+ }
265
+ signature := checker.GetResolvedSignature(node)
266
+ if signature == nil {
267
+ return typiaadapter.CallSite{}, false
268
+ }
269
+ declaration := signature.Declaration()
270
+ if declaration == nil {
271
+ return typiaadapter.CallSite{}, false
272
+ }
273
+ sourceFile := shimast.GetSourceFileOfNode(declaration)
274
+ if sourceFile == nil {
275
+ return typiaadapter.CallSite{}, false
276
+ }
277
+ module, ok := nestiaTypiaMatchModule(sourceFile.FileName())
278
+ if !ok {
279
+ return typiaadapter.CallSite{}, false
280
+ }
281
+ method := nestiaTypiaCallSiteMethodName(checker, declaration, call)
282
+ if method == "" {
283
+ return typiaadapter.CallSite{}, false
284
+ }
285
+ root, namespaces, ok := nestiaTypiaExtractRootAndNamespaces(call, method)
286
+ if !ok {
287
+ return typiaadapter.CallSite{}, false
288
+ }
289
+ return typiaadapter.CallSite{
290
+ File: file,
291
+ FilePath: file.FileName(),
292
+ Module: module,
293
+ Method: method,
294
+ Call: call,
295
+ RootName: root,
296
+ Namespaces: namespaces,
297
+ }, true
298
+ }
299
+
300
+ func nestiaTypiaMatchModule(location string) (string, bool) {
301
+ location = filepath.ToSlash(location)
302
+ for _, suffix := range []string{".d.ts", ".ts"} {
303
+ for _, middle := range []string{"typia/lib/", "typia/src/", "packages/typia/src/"} {
304
+ index := strings.LastIndex(location, middle)
305
+ if index < 0 {
306
+ continue
307
+ }
308
+ name := location[index+len(middle):]
309
+ if !strings.HasSuffix(name, suffix) {
310
+ continue
311
+ }
312
+ name = strings.TrimSuffix(name, suffix)
313
+ if strings.Contains(name, "/") {
314
+ continue
315
+ }
316
+ return name, true
317
+ }
318
+ }
319
+ return "", false
320
+ }
321
+
322
+ func nestiaTypiaCallSiteMethodName(checker *shimchecker.Checker, declaration *shimast.Node, call *shimast.CallExpression) string {
323
+ if name := declaration.Name(); name != nil {
324
+ if symbol := checker.GetSymbolAtLocation(name); symbol != nil && symbol.Name != "" {
325
+ return symbol.Name
326
+ }
327
+ if name.Kind == shimast.KindIdentifier {
328
+ if id := name.AsIdentifier(); id != nil {
329
+ return id.Text
330
+ }
331
+ }
332
+ }
333
+ if call.Expression != nil && call.Expression.Kind == shimast.KindPropertyAccessExpression {
334
+ if property := call.Expression.AsPropertyAccessExpression(); property != nil {
335
+ if name := property.Name(); name != nil && name.Kind == shimast.KindIdentifier {
336
+ if id := name.AsIdentifier(); id != nil {
337
+ return id.Text
338
+ }
339
+ }
340
+ }
341
+ }
342
+ return ""
343
+ }
344
+
345
+ func nestiaTypiaExtractRootAndNamespaces(call *shimast.CallExpression, method string) (string, []string, bool) {
346
+ expression := call.Expression
347
+ segments := []string{}
348
+ for expression != nil && expression.Kind == shimast.KindPropertyAccessExpression {
349
+ property := expression.AsPropertyAccessExpression()
350
+ if property == nil {
351
+ break
352
+ }
353
+ name := property.Name()
354
+ if name == nil || name.Kind != shimast.KindIdentifier {
355
+ return "", nil, false
356
+ }
357
+ id := name.AsIdentifier()
358
+ if id == nil || id.Text == "" {
359
+ return "", nil, false
360
+ }
361
+ segments = append([]string{id.Text}, segments...)
362
+ expression = property.Expression
363
+ }
364
+ if expression == nil || expression.Kind != shimast.KindIdentifier {
365
+ return "", nil, false
366
+ }
367
+ root := expression.AsIdentifier()
368
+ if root == nil || root.Text == "" || len(segments) == 0 {
369
+ return "", nil, false
370
+ }
371
+ if segments[len(segments)-1] != method {
372
+ return "", nil, false
373
+ }
374
+ return root.Text, segments[:len(segments)-1], true
375
+ }