@nestia/core 11.2.1 → 12.0.0-dev.20260521.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.
- package/MIGRATION.md +169 -0
- package/lib/adaptors/WebSocketAdaptor.js +7 -3
- package/lib/adaptors/WebSocketAdaptor.js.map +1 -1
- package/lib/decorators/DynamicModule.js.map +1 -1
- package/lib/decorators/EncryptedBody.js.map +1 -1
- package/lib/decorators/EncryptedController.js.map +1 -1
- package/lib/decorators/EncryptedModule.js.map +1 -1
- package/lib/decorators/EncryptedRoute.js.map +1 -1
- package/lib/decorators/HumanRoute.js.map +1 -1
- package/lib/decorators/NoTransformConfigurationError.js +5 -2
- package/lib/decorators/NoTransformConfigurationError.js.map +1 -1
- package/lib/decorators/PlainBody.js.map +1 -1
- package/lib/decorators/SwaggerCustomizer.js.map +1 -1
- package/lib/decorators/SwaggerExample.js.map +1 -1
- package/lib/decorators/TypedBody.js.map +1 -1
- package/lib/decorators/TypedException.js.map +1 -1
- package/lib/decorators/TypedFormData.js.map +1 -1
- package/lib/decorators/TypedHeaders.js.map +1 -1
- package/lib/decorators/TypedParam.js +5 -2
- package/lib/decorators/TypedParam.js.map +1 -1
- package/lib/decorators/TypedQuery.js.map +1 -1
- package/lib/decorators/TypedRoute.js.map +1 -1
- package/lib/decorators/WebSocketRoute.js.map +1 -1
- package/lib/decorators/doNotThrowTransformError.js.map +1 -1
- package/lib/decorators/internal/get_path_and_querify.js +5 -2
- package/lib/decorators/internal/get_path_and_querify.js.map +1 -1
- package/lib/decorators/internal/get_path_and_stringify.js +5 -2
- package/lib/decorators/internal/get_path_and_stringify.js.map +1 -1
- package/lib/decorators/internal/get_text_body.js.map +1 -1
- package/lib/decorators/internal/headers_to_object.js.map +1 -1
- package/lib/decorators/internal/is_request_body_undefined.js.map +1 -1
- package/lib/decorators/internal/load_controller.js +48 -16
- package/lib/decorators/internal/load_controller.js.map +1 -1
- package/lib/decorators/internal/route_error.js +3 -3
- package/lib/decorators/internal/route_error.js.map +1 -1
- package/lib/decorators/internal/validate_request_body.js +5 -2
- package/lib/decorators/internal/validate_request_body.js.map +1 -1
- package/lib/decorators/internal/validate_request_form_data.js +5 -2
- package/lib/decorators/internal/validate_request_form_data.js.map +1 -1
- package/lib/decorators/internal/validate_request_headers.js +5 -2
- package/lib/decorators/internal/validate_request_headers.js.map +1 -1
- package/lib/decorators/internal/validate_request_query.js +30 -4
- package/lib/decorators/internal/validate_request_query.js.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/transform.d.ts +10 -5
- package/lib/transform.js +64 -28
- package/lib/transform.js.map +1 -1
- package/lib/utils/ArrayUtil.js.map +1 -1
- package/lib/utils/ExceptionManager.js.map +1 -1
- package/lib/utils/Singleton.js.map +1 -1
- package/lib/utils/SourceFinder.js.map +1 -1
- package/lib/utils/VersioningStrategy.js.map +1 -1
- package/native/cmd/ttsc-nestia/main.go +11 -0
- package/native/go.mod +32 -0
- package/native/go.sum +54 -0
- package/native/plugin/plan.go +102 -0
- package/native/transform/ast.go +32 -0
- package/native/transform/build.go +413 -0
- package/native/transform/cleanup.go +408 -0
- package/native/transform/cleanup_test.go +76 -0
- package/native/transform/commonjs_import_alias_test.go +49 -0
- package/native/transform/core_dispatch_test.go +127 -0
- package/native/transform/core_querify.go +227 -0
- package/native/transform/core_transform.go +1713 -0
- package/native/transform/core_websocket.go +115 -0
- package/native/transform/exports.go +13 -0
- package/native/transform/path_rewrite.go +285 -0
- package/native/transform/path_rewrite_test.go +243 -0
- package/native/transform/printer.go +244 -0
- package/native/transform/rewrite.go +662 -0
- package/native/transform/rewrite_test.go +118 -0
- package/native/transform/rewrite_unique_base_test.go +48 -0
- package/native/transform/run.go +72 -0
- package/native/transform/transform.go +376 -0
- package/native/transform/typia_fast.go +326 -0
- package/native/transform/typia_replacement.go +24 -0
- package/native/transform.cjs +43 -0
- package/package.json +28 -22
- package/src/decorators/NoTransformConfigurationError.ts +5 -2
- package/src/decorators/internal/load_controller.ts +50 -19
- package/src/decorators/internal/validate_request_query.ts +21 -2
- package/src/transform.ts +101 -35
- package/lib/decorators/internal/NoTransformConfigureError.d.ts +0 -1
- package/lib/decorators/internal/NoTransformConfigureError.js +0 -7
- package/lib/decorators/internal/NoTransformConfigureError.js.map +0 -1
- package/lib/options/INestiaTransformOptions.d.ts +0 -13
- package/lib/options/INestiaTransformOptions.js +0 -3
- package/lib/options/INestiaTransformOptions.js.map +0 -1
- package/lib/options/INestiaTransformProject.d.ts +0 -5
- package/lib/options/INestiaTransformProject.js +0 -3
- package/lib/options/INestiaTransformProject.js.map +0 -1
- package/lib/programmers/PlainBodyProgrammer.d.ts +0 -9
- package/lib/programmers/PlainBodyProgrammer.js +0 -58
- package/lib/programmers/PlainBodyProgrammer.js.map +0 -1
- package/lib/programmers/TypedBodyProgrammer.d.ts +0 -9
- package/lib/programmers/TypedBodyProgrammer.js +0 -94
- package/lib/programmers/TypedBodyProgrammer.js.map +0 -1
- package/lib/programmers/TypedFormDataBodyProgrammer.d.ts +0 -9
- package/lib/programmers/TypedFormDataBodyProgrammer.js +0 -65
- package/lib/programmers/TypedFormDataBodyProgrammer.js.map +0 -1
- package/lib/programmers/TypedHeadersProgrammer.d.ts +0 -9
- package/lib/programmers/TypedHeadersProgrammer.js +0 -38
- package/lib/programmers/TypedHeadersProgrammer.js.map +0 -1
- package/lib/programmers/TypedParamProgrammer.d.ts +0 -10
- package/lib/programmers/TypedParamProgrammer.js +0 -32
- package/lib/programmers/TypedParamProgrammer.js.map +0 -1
- package/lib/programmers/TypedQueryBodyProgrammer.d.ts +0 -9
- package/lib/programmers/TypedQueryBodyProgrammer.js +0 -74
- package/lib/programmers/TypedQueryBodyProgrammer.js.map +0 -1
- package/lib/programmers/TypedQueryProgrammer.d.ts +0 -9
- package/lib/programmers/TypedQueryProgrammer.js +0 -75
- package/lib/programmers/TypedQueryProgrammer.js.map +0 -1
- package/lib/programmers/TypedQueryRouteProgrammer.d.ts +0 -9
- package/lib/programmers/TypedQueryRouteProgrammer.js +0 -75
- package/lib/programmers/TypedQueryRouteProgrammer.js.map +0 -1
- package/lib/programmers/TypedRouteProgrammer.d.ts +0 -9
- package/lib/programmers/TypedRouteProgrammer.js +0 -79
- package/lib/programmers/TypedRouteProgrammer.js.map +0 -1
- package/lib/programmers/http/HttpAssertQuerifyProgrammer.d.ts +0 -9
- package/lib/programmers/http/HttpAssertQuerifyProgrammer.js +0 -39
- package/lib/programmers/http/HttpAssertQuerifyProgrammer.js.map +0 -1
- package/lib/programmers/http/HttpIsQuerifyProgrammer.d.ts +0 -9
- package/lib/programmers/http/HttpIsQuerifyProgrammer.js +0 -36
- package/lib/programmers/http/HttpIsQuerifyProgrammer.js.map +0 -1
- package/lib/programmers/http/HttpQuerifyProgrammer.d.ts +0 -9
- package/lib/programmers/http/HttpQuerifyProgrammer.js +0 -50
- package/lib/programmers/http/HttpQuerifyProgrammer.js.map +0 -1
- package/lib/programmers/http/HttpValidateQuerifyProgrammer.d.ts +0 -9
- package/lib/programmers/http/HttpValidateQuerifyProgrammer.js +0 -40
- package/lib/programmers/http/HttpValidateQuerifyProgrammer.js.map +0 -1
- package/lib/programmers/internal/CoreMetadataUtil.d.ts +0 -5
- package/lib/programmers/internal/CoreMetadataUtil.js +0 -19
- package/lib/programmers/internal/CoreMetadataUtil.js.map +0 -1
- package/lib/transformers/FileTransformer.d.ts +0 -5
- package/lib/transformers/FileTransformer.js +0 -80
- package/lib/transformers/FileTransformer.js.map +0 -1
- package/lib/transformers/MethodTransformer.d.ts +0 -8
- package/lib/transformers/MethodTransformer.js +0 -58
- package/lib/transformers/MethodTransformer.js.map +0 -1
- package/lib/transformers/NodeTransformer.d.ts +0 -8
- package/lib/transformers/NodeTransformer.js +0 -24
- package/lib/transformers/NodeTransformer.js.map +0 -1
- package/lib/transformers/ParameterDecoratorTransformer.d.ts +0 -9
- package/lib/transformers/ParameterDecoratorTransformer.js +0 -104
- package/lib/transformers/ParameterDecoratorTransformer.js.map +0 -1
- package/lib/transformers/ParameterTransformer.d.ts +0 -8
- package/lib/transformers/ParameterTransformer.js +0 -37
- package/lib/transformers/ParameterTransformer.js.map +0 -1
- package/lib/transformers/TypedRouteTransformer.d.ts +0 -9
- package/lib/transformers/TypedRouteTransformer.js +0 -68
- package/lib/transformers/TypedRouteTransformer.js.map +0 -1
- package/lib/transformers/WebSocketRouteTransformer.d.ts +0 -9
- package/lib/transformers/WebSocketRouteTransformer.js +0 -72
- package/lib/transformers/WebSocketRouteTransformer.js.map +0 -1
- package/src/decorators/internal/NoTransformConfigureError.ts +0 -2
- package/src/options/INestiaTransformOptions.ts +0 -34
- package/src/options/INestiaTransformProject.ts +0 -10
- package/src/programmers/PlainBodyProgrammer.ts +0 -72
- package/src/programmers/TypedBodyProgrammer.ts +0 -148
- package/src/programmers/TypedFormDataBodyProgrammer.ts +0 -118
- package/src/programmers/TypedHeadersProgrammer.ts +0 -65
- package/src/programmers/TypedParamProgrammer.ts +0 -33
- package/src/programmers/TypedQueryBodyProgrammer.ts +0 -113
- package/src/programmers/TypedQueryProgrammer.ts +0 -115
- package/src/programmers/TypedQueryRouteProgrammer.ts +0 -107
- package/src/programmers/TypedRouteProgrammer.ts +0 -103
- package/src/programmers/http/HttpAssertQuerifyProgrammer.ts +0 -74
- package/src/programmers/http/HttpIsQuerifyProgrammer.ts +0 -77
- package/src/programmers/http/HttpQuerifyProgrammer.ts +0 -110
- package/src/programmers/http/HttpValidateQuerifyProgrammer.ts +0 -78
- package/src/programmers/internal/CoreMetadataUtil.ts +0 -21
- package/src/transformers/FileTransformer.ts +0 -109
- package/src/transformers/MethodTransformer.ts +0 -103
- package/src/transformers/NodeTransformer.ts +0 -23
- package/src/transformers/ParameterDecoratorTransformer.ts +0 -143
- package/src/transformers/ParameterTransformer.ts +0 -57
- package/src/transformers/TypedRouteTransformer.ts +0 -85
- package/src/transformers/WebSocketRouteTransformer.ts +0 -120
|
@@ -0,0 +1,326 @@
|
|
|
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
|
+
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
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
package transform
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
|
|
6
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
7
|
+
typiaadapter "github.com/samchon/typia/packages/typia/native/adapter"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
func parenthesizeTypiaReplacement(
|
|
11
|
+
site typiaadapter.CallSite,
|
|
12
|
+
expr string,
|
|
13
|
+
) string {
|
|
14
|
+
text := strings.TrimSpace(expr)
|
|
15
|
+
if strings.HasPrefix(text, "{") == false {
|
|
16
|
+
return expr
|
|
17
|
+
}
|
|
18
|
+
node := site.Call.AsNode()
|
|
19
|
+
if node == nil || node.Parent == nil ||
|
|
20
|
+
node.Parent.Kind != shimast.KindExpressionStatement {
|
|
21
|
+
return expr
|
|
22
|
+
}
|
|
23
|
+
return "(" + expr + ")"
|
|
24
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const fs = require("node:fs");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
|
|
4
|
+
// `@nestia/core` ttsc plugin descriptor.
|
|
5
|
+
//
|
|
6
|
+
// `source` is the Go command package of the executable transform host
|
|
7
|
+
// (`cmd/ttsc-nestia`, package `main`).
|
|
8
|
+
//
|
|
9
|
+
// `@nestia/sdk` is NOT a standalone ttsc plugin: its Go transform is declared
|
|
10
|
+
// here as a `contributor`, discovered by resolving `@nestia/sdk` from the
|
|
11
|
+
// project. ttsc statically links a contributor's Go source into this host
|
|
12
|
+
// binary. Consequences:
|
|
13
|
+
//
|
|
14
|
+
// - A project that depends on `@nestia/core` but not `@nestia/sdk` never
|
|
15
|
+
// links, compiles, or ships any SDK transform code.
|
|
16
|
+
// - When the `@nestia/core` plugin itself is disabled, this descriptor is
|
|
17
|
+
// never evaluated, so the SDK contributor is never linked either.
|
|
18
|
+
function createTtscPlugin(context) {
|
|
19
|
+
const plugin = {
|
|
20
|
+
name: "@nestia/core",
|
|
21
|
+
source: path.resolve(__dirname, "cmd", "ttsc-nestia"),
|
|
22
|
+
composes: ["typia/lib/transform"],
|
|
23
|
+
};
|
|
24
|
+
const sdk = resolveSdkContributorSource(context);
|
|
25
|
+
if (sdk !== null) plugin.contributors = [{ name: "sdk", source: sdk }];
|
|
26
|
+
return plugin;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function resolveSdkContributorSource(context) {
|
|
30
|
+
const paths = [__dirname];
|
|
31
|
+
if (context && typeof context.projectRoot === "string")
|
|
32
|
+
paths.push(context.projectRoot);
|
|
33
|
+
try {
|
|
34
|
+
const manifest = require.resolve("@nestia/sdk/package.json", { paths });
|
|
35
|
+
const source = path.resolve(path.dirname(manifest), "native", "sdk");
|
|
36
|
+
return fs.existsSync(source) ? source : null;
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = createTtscPlugin;
|
|
43
|
+
module.exports.createTtscPlugin = createTtscPlugin;
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestia/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "12.0.0-dev.20260521.1",
|
|
4
4
|
"description": "Super-fast validation decorators of NestJS",
|
|
5
|
+
"types": "lib/index.d.ts",
|
|
5
6
|
"main": "lib/index.js",
|
|
6
7
|
"exports": {
|
|
7
8
|
".": {
|
|
@@ -12,6 +13,7 @@
|
|
|
12
13
|
"types": "./lib/transform.d.ts",
|
|
13
14
|
"default": "./lib/transform.js"
|
|
14
15
|
},
|
|
16
|
+
"./native/transform.cjs": "./native/transform.cjs",
|
|
15
17
|
"./package.json": "./package.json"
|
|
16
18
|
},
|
|
17
19
|
"tsp": {
|
|
@@ -19,6 +21,11 @@
|
|
|
19
21
|
"parseAllJsDoc": true
|
|
20
22
|
}
|
|
21
23
|
},
|
|
24
|
+
"ttsc": {
|
|
25
|
+
"plugin": {
|
|
26
|
+
"transform": "@nestia/core/native/transform.cjs"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
22
29
|
"repository": {
|
|
23
30
|
"type": "git",
|
|
24
31
|
"url": "https://github.com/samchon/nestia"
|
|
@@ -39,10 +46,8 @@
|
|
|
39
46
|
},
|
|
40
47
|
"homepage": "https://nestia.io",
|
|
41
48
|
"dependencies": {
|
|
42
|
-
"@typia/
|
|
43
|
-
"@typia/
|
|
44
|
-
"@typia/utils": "12.1.0",
|
|
45
|
-
"detect-ts-node": "^1.0.5",
|
|
49
|
+
"@typia/interface": "13.0.0-dev.20260521.1",
|
|
50
|
+
"@typia/utils": "13.0.0-dev.20260521.1",
|
|
46
51
|
"get-function-location": "^2.0.0",
|
|
47
52
|
"glob": "^11.0.3",
|
|
48
53
|
"path-parser": "^6.1.0",
|
|
@@ -50,50 +55,51 @@
|
|
|
50
55
|
"reflect-metadata": ">=0.1.12",
|
|
51
56
|
"rxjs": ">=6.0.3",
|
|
52
57
|
"tgrid": "^1.1.0",
|
|
53
|
-
"typia": "
|
|
58
|
+
"typia": "13.0.0-dev.20260521.1",
|
|
54
59
|
"ws": "^7.5.3",
|
|
55
|
-
"@nestia/fetcher": "^
|
|
60
|
+
"@nestia/fetcher": "^12.0.0-dev.20260521.1"
|
|
56
61
|
},
|
|
57
62
|
"peerDependencies": {
|
|
58
63
|
"@nestjs/common": ">=7.0.1",
|
|
59
64
|
"@nestjs/core": ">=7.0.1",
|
|
60
65
|
"reflect-metadata": ">=0.1.12",
|
|
61
66
|
"rxjs": ">=6.0.3",
|
|
62
|
-
"typia": "
|
|
63
|
-
"@nestia/fetcher": "^
|
|
67
|
+
"typia": "13.0.0-dev.20260521.1",
|
|
68
|
+
"@nestia/fetcher": "^12.0.0-dev.20260521.1"
|
|
64
69
|
},
|
|
65
70
|
"devDependencies": {
|
|
66
71
|
"@nestjs/common": "^11.1.6",
|
|
67
72
|
"@nestjs/core": "^11.1.6",
|
|
68
73
|
"@nestjs/platform-express": "^11.1.6",
|
|
74
|
+
"@typescript/native-preview": "7.0.0-dev.20260518.1",
|
|
69
75
|
"@types/express": "^4.17.15",
|
|
70
|
-
"@types/inquirer": "^9.0.3",
|
|
71
76
|
"@types/multer": "^1.4.12",
|
|
72
77
|
"@types/ws": "^8.5.10",
|
|
73
|
-
"commander": "^10.0.0",
|
|
74
|
-
"comment-json": "^4.2.3",
|
|
75
|
-
"eslint-plugin-deprecation": "^1.4.1",
|
|
76
78
|
"fastify": "^4.28.1",
|
|
77
|
-
"git-last-commit": "^1.0.1",
|
|
78
|
-
"inquirer": "^8.2.5",
|
|
79
79
|
"rimraf": "^6.1.3",
|
|
80
|
-
"
|
|
81
|
-
"tstl": "^3.0.0"
|
|
82
|
-
"typescript": "~6.0.3"
|
|
80
|
+
"ttsc": "^0.12.3",
|
|
81
|
+
"tstl": "^3.0.0"
|
|
83
82
|
},
|
|
84
83
|
"files": [
|
|
85
84
|
"README.md",
|
|
85
|
+
"MIGRATION.md",
|
|
86
86
|
"LICENSE",
|
|
87
87
|
"package.json",
|
|
88
88
|
"lib",
|
|
89
|
+
"native/cmd",
|
|
90
|
+
"native/go.mod",
|
|
91
|
+
"native/go.sum",
|
|
92
|
+
"native/plugin",
|
|
93
|
+
"native/transform",
|
|
94
|
+
"native/transform.cjs",
|
|
89
95
|
"src"
|
|
90
96
|
],
|
|
91
97
|
"publishConfig": {
|
|
92
98
|
"access": "public"
|
|
93
99
|
},
|
|
94
100
|
"scripts": {
|
|
95
|
-
"build": "rimraf lib &&
|
|
96
|
-
"dev": "
|
|
97
|
-
|
|
98
|
-
|
|
101
|
+
"build": "rimraf lib && ttsc",
|
|
102
|
+
"dev": "ttsc --watch",
|
|
103
|
+
"test:go": "cd test && go test ./..."
|
|
104
|
+
}
|
|
99
105
|
}
|
|
@@ -4,8 +4,11 @@ export function NoTransformConfigurationError(method: string) {
|
|
|
4
4
|
throw new Error(
|
|
5
5
|
[
|
|
6
6
|
`Error on nestia.core.${method}(): no transform has been configured.`,
|
|
7
|
-
`
|
|
8
|
-
`
|
|
7
|
+
`Build the project with "ttsc" (not stock "tsc"); the Go-backed @nestia/core transform attaches automatically through ttsc plugin auto-discovery.`,
|
|
8
|
+
`If "ttsc" is missing, run "npx nestia setup" to install the toolchain and runtime packages.`,
|
|
9
|
+
`If you're using a non-standard TypeScript compiler like Babel or SWC, the @nestia/core transformer is not available.`,
|
|
10
|
+
`If you're running "npx nestia sdk/swagger" or similar, run "npx ttsc --noEmit" to surface the underlying compilation error.`,
|
|
11
|
+
`See https://nestia.io/docs/setup for the full setup (tsgo and legacy paths); migration notes are at node_modules/@nestia/core/MIGRATION.md.`,
|
|
9
12
|
].join(" "),
|
|
10
13
|
);
|
|
11
14
|
return undefined as never;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
2
3
|
|
|
3
4
|
import { Creator } from "../../typings/Creator";
|
|
4
5
|
import { SourceFinder } from "../../utils/SourceFinder";
|
|
@@ -7,24 +8,36 @@ export const load_controllers = async (
|
|
|
7
8
|
path: string | string[] | { include: string[]; exclude?: string[] },
|
|
8
9
|
isTsNode?: boolean,
|
|
9
10
|
): Promise<Creator<object>[]> => {
|
|
11
|
+
const include: string[] = Array.isArray(path)
|
|
12
|
+
? path
|
|
13
|
+
: typeof path === "object"
|
|
14
|
+
? path.include
|
|
15
|
+
: [path];
|
|
16
|
+
const exclude: string[] =
|
|
17
|
+
typeof path === "object" && !Array.isArray(path) ? (path.exclude ?? []) : [];
|
|
18
|
+
const filter =
|
|
19
|
+
isTsNode === true
|
|
20
|
+
? (file: string) =>
|
|
21
|
+
file.substring(file.length - 3) === ".ts" &&
|
|
22
|
+
file.substring(file.length - 5) !== ".d.ts"
|
|
23
|
+
: (file: string) => file.substring(file.length - 3) === ".js";
|
|
10
24
|
const sources: string[] = await SourceFinder.find({
|
|
11
|
-
include
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
? path.include
|
|
15
|
-
: [path],
|
|
16
|
-
exclude:
|
|
17
|
-
typeof path === "object" && !Array.isArray(path)
|
|
18
|
-
? (path.exclude ?? [])
|
|
19
|
-
: [],
|
|
20
|
-
filter:
|
|
21
|
-
isTsNode === true || EXTENSION === "ts"
|
|
22
|
-
? (file) =>
|
|
23
|
-
file.substring(file.length - 3) === ".ts" &&
|
|
24
|
-
file.substring(file.length - 5) !== ".d.ts"
|
|
25
|
-
: (flle) => flle.substring(flle.length - 3) === ".js",
|
|
25
|
+
include,
|
|
26
|
+
exclude,
|
|
27
|
+
filter,
|
|
26
28
|
});
|
|
27
|
-
|
|
29
|
+
const controllers: Creator<object>[] = await mount(sources);
|
|
30
|
+
if (controllers.length !== 0 || isTsNode === true) return controllers;
|
|
31
|
+
|
|
32
|
+
const runtimeRoot: string | null = findTtsxRuntimeRoot();
|
|
33
|
+
if (runtimeRoot === null) return controllers;
|
|
34
|
+
|
|
35
|
+
const fallback: string[] = await SourceFinder.find({
|
|
36
|
+
include: include.map((p) => resolveRuntimePattern(runtimeRoot, p)),
|
|
37
|
+
exclude: exclude.map((p) => resolveRuntimePattern(runtimeRoot, p)),
|
|
38
|
+
filter,
|
|
39
|
+
});
|
|
40
|
+
return fallback.length === 0 ? controllers : mount(fallback);
|
|
28
41
|
};
|
|
29
42
|
|
|
30
43
|
/** @internal */
|
|
@@ -41,5 +54,23 @@ async function mount(sources: string[]): Promise<any[]> {
|
|
|
41
54
|
return controllers;
|
|
42
55
|
}
|
|
43
56
|
|
|
44
|
-
|
|
45
|
-
const
|
|
57
|
+
function findTtsxRuntimeRoot(): string | null {
|
|
58
|
+
const entry: string | undefined = process.argv[1];
|
|
59
|
+
if (entry === undefined) return null;
|
|
60
|
+
|
|
61
|
+
let current: string = path.resolve(entry);
|
|
62
|
+
if (fs.existsSync(current) && fs.statSync(current).isFile())
|
|
63
|
+
current = path.dirname(current);
|
|
64
|
+
while (true) {
|
|
65
|
+
if (fs.existsSync(path.join(current, ".ttsx.tsconfig.json")))
|
|
66
|
+
return current;
|
|
67
|
+
const parent: string = path.dirname(current);
|
|
68
|
+
if (parent === current) return null;
|
|
69
|
+
current = parent;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function resolveRuntimePattern(runtimeRoot: string, pattern: string): string {
|
|
74
|
+
if (path.isAbsolute(pattern)) return pattern;
|
|
75
|
+
return path.resolve(runtimeRoot, pattern);
|
|
76
|
+
}
|
|
@@ -35,6 +35,7 @@ const assert =
|
|
|
35
35
|
message: MESSAGE,
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
+
if (is_missing_query_property(exp)) return new BadRequestException(MESSAGE);
|
|
38
39
|
throw exp;
|
|
39
40
|
}
|
|
40
41
|
};
|
|
@@ -43,7 +44,14 @@ const assert =
|
|
|
43
44
|
const is =
|
|
44
45
|
<T>(closure: (input: URLSearchParams) => T | null) =>
|
|
45
46
|
(input: URLSearchParams): T | BadRequestException => {
|
|
46
|
-
const result: T | null =
|
|
47
|
+
const result: T | null = (() => {
|
|
48
|
+
try {
|
|
49
|
+
return closure(input);
|
|
50
|
+
} catch (exp) {
|
|
51
|
+
if (is_missing_query_property(exp)) return null;
|
|
52
|
+
throw exp;
|
|
53
|
+
}
|
|
54
|
+
})();
|
|
47
55
|
return result !== null ? result : new BadRequestException(MESSAGE);
|
|
48
56
|
};
|
|
49
57
|
|
|
@@ -51,7 +59,15 @@ const is =
|
|
|
51
59
|
const validate =
|
|
52
60
|
<T>(closure: (input: URLSearchParams) => IValidation<T>) =>
|
|
53
61
|
(input: URLSearchParams): T | BadRequestException => {
|
|
54
|
-
const result: IValidation<T> =
|
|
62
|
+
const result: IValidation<T> | null = (() => {
|
|
63
|
+
try {
|
|
64
|
+
return closure(input);
|
|
65
|
+
} catch (exp) {
|
|
66
|
+
if (is_missing_query_property(exp)) return null;
|
|
67
|
+
throw exp;
|
|
68
|
+
}
|
|
69
|
+
})();
|
|
70
|
+
if (result === null) return new BadRequestException(MESSAGE);
|
|
55
71
|
return result.success
|
|
56
72
|
? result.data
|
|
57
73
|
: new BadRequestException({
|
|
@@ -62,3 +78,6 @@ const validate =
|
|
|
62
78
|
|
|
63
79
|
/** @internal */
|
|
64
80
|
const MESSAGE = "Request query data is not following the promised type.";
|
|
81
|
+
|
|
82
|
+
const is_missing_query_property = (exp: unknown): exp is Error =>
|
|
83
|
+
exp instanceof Error && /^missing [^.\[\]\s]+$/.test(exp.message);
|