@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.
- 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 +2 -5
- package/lib/transform.js +48 -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/build.go +434 -0
- package/native/cmd/ttsc-nestia/cleanup.go +408 -0
- package/native/cmd/ttsc-nestia/core_querify.go +227 -0
- package/native/cmd/ttsc-nestia/core_transform.go +1713 -0
- package/native/cmd/ttsc-nestia/core_websocket.go +115 -0
- package/native/cmd/ttsc-nestia/main.go +72 -0
- package/native/cmd/ttsc-nestia/path_rewrite.go +285 -0
- package/native/cmd/ttsc-nestia/printer.go +244 -0
- package/native/cmd/ttsc-nestia/rewrite.go +662 -0
- package/native/cmd/ttsc-nestia/sdk_metadata_json.go +327 -0
- package/native/cmd/ttsc-nestia/sdk_transform.go +1541 -0
- package/native/cmd/ttsc-nestia/transform.go +387 -0
- package/native/cmd/ttsc-nestia/typia_fast.go +326 -0
- package/native/cmd/ttsc-nestia/typia_replacement.go +24 -0
- package/native/go.mod +32 -0
- package/native/go.sum +54 -0
- package/native/plugin/plan.go +102 -0
- package/native/transform.cjs +21 -0
- package/package.json +27 -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 +82 -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,1541 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"fmt"
|
|
6
|
+
"os"
|
|
7
|
+
"path/filepath"
|
|
8
|
+
"regexp"
|
|
9
|
+
"sort"
|
|
10
|
+
"strings"
|
|
11
|
+
|
|
12
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
13
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
14
|
+
shimscanner "github.com/microsoft/typescript-go/shim/scanner"
|
|
15
|
+
"github.com/samchon/nestia/packages/core/native/plugin"
|
|
16
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
17
|
+
nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
|
|
18
|
+
nativefactories "github.com/samchon/typia/packages/typia/native/core/factories"
|
|
19
|
+
nativejson "github.com/samchon/typia/packages/typia/native/core/programmers/json"
|
|
20
|
+
schemametadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
type nestiaSDKSite struct {
|
|
24
|
+
File *shimast.SourceFile
|
|
25
|
+
FilePath string
|
|
26
|
+
ClassName string
|
|
27
|
+
MethodName string
|
|
28
|
+
Method *shimast.Node
|
|
29
|
+
Metadata string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type nestiaSDKBuildRewriteSet struct {
|
|
33
|
+
byPath map[string][]nestiaSDKSite
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type nestiaSDKContext struct {
|
|
37
|
+
prog *driver.Program
|
|
38
|
+
collection *schemametadata.MetadataCollection
|
|
39
|
+
importsByFile map[string][]nestiaSDKImportInfo
|
|
40
|
+
schemaCache map[nestiaSDKSchemaCacheKey]any
|
|
41
|
+
schemaHits int
|
|
42
|
+
schemaMisses int
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type nestiaSDKSchemaCacheKey struct {
|
|
46
|
+
Type *shimchecker.Type
|
|
47
|
+
Text string
|
|
48
|
+
Escape bool
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func newNestiaSDKContext(prog *driver.Program) *nestiaSDKContext {
|
|
52
|
+
return &nestiaSDKContext{
|
|
53
|
+
prog: prog,
|
|
54
|
+
collection: newNestiaSDKMetadataCollection(),
|
|
55
|
+
importsByFile: map[string][]nestiaSDKImportInfo{},
|
|
56
|
+
schemaCache: map[nestiaSDKSchemaCacheKey]any{},
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
func newNestiaSDKMetadataCollection() *schemametadata.MetadataCollection {
|
|
61
|
+
return schemametadata.NewMetadataCollection(
|
|
62
|
+
&schemametadata.MetadataCollection_IOptions{
|
|
63
|
+
Replace: schemametadata.MetadataCollection_replace,
|
|
64
|
+
},
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
func (ctx *nestiaSDKContext) imports(file *shimast.SourceFile) []nestiaSDKImportInfo {
|
|
69
|
+
if file == nil {
|
|
70
|
+
return nil
|
|
71
|
+
}
|
|
72
|
+
name := filepath.ToSlash(file.FileName())
|
|
73
|
+
if imports, ok := ctx.importsByFile[name]; ok {
|
|
74
|
+
return imports
|
|
75
|
+
}
|
|
76
|
+
imports := nestiaSDKAnalyzeImports(file)
|
|
77
|
+
ctx.importsByFile[name] = imports
|
|
78
|
+
return imports
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
func newNestiaSDKBuildRewriteSet() *nestiaSDKBuildRewriteSet {
|
|
82
|
+
return &nestiaSDKBuildRewriteSet{byPath: map[string][]nestiaSDKSite{}}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
func (rs *nestiaSDKBuildRewriteSet) Add(site nestiaSDKSite) {
|
|
86
|
+
if site.FilePath == "" {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
path := filepath.ToSlash(site.FilePath)
|
|
90
|
+
rs.byPath[path] = append(rs.byPath[path], site)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
func (rs *nestiaSDKBuildRewriteSet) Len() int {
|
|
94
|
+
if rs == nil {
|
|
95
|
+
return 0
|
|
96
|
+
}
|
|
97
|
+
count := 0
|
|
98
|
+
for _, sites := range rs.byPath {
|
|
99
|
+
count += len(sites)
|
|
100
|
+
}
|
|
101
|
+
return count
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
func (rs *nestiaSDKBuildRewriteSet) Apply(outputName string, text string) (string, error) {
|
|
105
|
+
if rs == nil || len(rs.byPath) == 0 {
|
|
106
|
+
return text, nil
|
|
107
|
+
}
|
|
108
|
+
srcPath, ok := rs.findSourceForOutput(outputName)
|
|
109
|
+
if !ok {
|
|
110
|
+
return text, nil
|
|
111
|
+
}
|
|
112
|
+
out := text
|
|
113
|
+
out = insertSDKOperationMetadataDecorators(out, rs.byPath[srcPath])
|
|
114
|
+
return injectSDKOperationMetadataImport(out), nil
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
func (rs *nestiaSDKBuildRewriteSet) findSourceForOutput(outputName string) (string, bool) {
|
|
118
|
+
outSlash := strings.TrimSuffix(filepath.ToSlash(outputName), filepath.Ext(outputName))
|
|
119
|
+
for path := range rs.byPath {
|
|
120
|
+
srcStem := strings.TrimSuffix(filepath.ToSlash(path), filepath.Ext(path))
|
|
121
|
+
if outputMatchesSourceStem(outSlash, srcStem) {
|
|
122
|
+
return path, true
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return "", false
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
func collectNestiaSDKSourceRewriteMap(
|
|
129
|
+
prog *driver.Program,
|
|
130
|
+
plan plugin.Plan,
|
|
131
|
+
onlyFile string,
|
|
132
|
+
) (map[string][]transformSourceRewrite, []typiaTransformDiagnostic) {
|
|
133
|
+
if plan.SDK == false {
|
|
134
|
+
return map[string][]transformSourceRewrite{}, nil
|
|
135
|
+
}
|
|
136
|
+
sites, diagnostics := collectNestiaSDKSites(prog)
|
|
137
|
+
rewrites := map[string][]transformSourceRewrite{}
|
|
138
|
+
touched := map[string]bool{}
|
|
139
|
+
for _, site := range sites {
|
|
140
|
+
file := filepath.ToSlash(site.FilePath)
|
|
141
|
+
if onlyFile != "" && file != filepath.ToSlash(onlyFile) {
|
|
142
|
+
continue
|
|
143
|
+
}
|
|
144
|
+
source, ok := sourceFileText(site.File)
|
|
145
|
+
if !ok {
|
|
146
|
+
diagnostics = append(diagnostics, nestiaSDKDiagnostic(site, "source text is unavailable"))
|
|
147
|
+
continue
|
|
148
|
+
}
|
|
149
|
+
insert := site.Method.Pos()
|
|
150
|
+
if decorators := site.Method.Decorators(); len(decorators) != 0 {
|
|
151
|
+
insert = decorators[0].Pos()
|
|
152
|
+
}
|
|
153
|
+
indent := sourceLineIndent(source, insert)
|
|
154
|
+
rewrites[file] = append(rewrites[file], transformSourceRewrite{
|
|
155
|
+
start: insert,
|
|
156
|
+
end: insert,
|
|
157
|
+
replacement: "\n" + indent + "@__OperationMetadata.OperationMetadata(" + site.Metadata + " as any)\n",
|
|
158
|
+
})
|
|
159
|
+
touched[file] = true
|
|
160
|
+
}
|
|
161
|
+
for file := range touched {
|
|
162
|
+
rewrites[file] = append(rewrites[file], transformSourceRewrite{
|
|
163
|
+
start: 0,
|
|
164
|
+
end: 0,
|
|
165
|
+
replacement: "import * as __OperationMetadata from \"@nestia/sdk\";\n",
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
return rewrites, diagnostics
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
func collectNestiaSDKBuildRewrites(
|
|
172
|
+
prog *driver.Program,
|
|
173
|
+
plan plugin.Plan,
|
|
174
|
+
) (*nestiaSDKBuildRewriteSet, []typiaTransformDiagnostic) {
|
|
175
|
+
set := newNestiaSDKBuildRewriteSet()
|
|
176
|
+
if plan.SDK == false {
|
|
177
|
+
return set, nil
|
|
178
|
+
}
|
|
179
|
+
sites, diagnostics := collectNestiaSDKSites(prog)
|
|
180
|
+
for _, site := range sites {
|
|
181
|
+
set.Add(site)
|
|
182
|
+
}
|
|
183
|
+
return set, diagnostics
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
func collectNestiaSDKSites(prog *driver.Program) ([]nestiaSDKSite, []typiaTransformDiagnostic) {
|
|
187
|
+
sites := []nestiaSDKSite{}
|
|
188
|
+
diagnostics := []typiaTransformDiagnostic{}
|
|
189
|
+
context := newNestiaSDKContext(prog)
|
|
190
|
+
for _, file := range prog.SourceFiles() {
|
|
191
|
+
if file == nil || file.IsDeclarationFile {
|
|
192
|
+
continue
|
|
193
|
+
}
|
|
194
|
+
visited := map[string]bool{}
|
|
195
|
+
file.ForEachChild(func(node *shimast.Node) bool {
|
|
196
|
+
visitNestiaSDKNode(context, file, node, visited, &sites, &diagnostics)
|
|
197
|
+
return false
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
if os.Getenv("TTSC_NESTIA_PROFILE") != "" {
|
|
201
|
+
fmt.Fprintf(stderr, "ttsc-nestia profile: sdk-schema-cache hits=%d misses=%d\n", context.schemaHits, context.schemaMisses)
|
|
202
|
+
}
|
|
203
|
+
return sites, diagnostics
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
func visitNestiaSDKNode(
|
|
207
|
+
context *nestiaSDKContext,
|
|
208
|
+
file *shimast.SourceFile,
|
|
209
|
+
node *shimast.Node,
|
|
210
|
+
visited map[string]bool,
|
|
211
|
+
sites *[]nestiaSDKSite,
|
|
212
|
+
diagnostics *[]typiaTransformDiagnostic,
|
|
213
|
+
) {
|
|
214
|
+
if node == nil {
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
if node.Kind == shimast.KindMethodDeclaration && len(node.Decorators()) != 0 {
|
|
218
|
+
className := nestiaSDKParentClassName(node)
|
|
219
|
+
methodName := nestiaSDKMethodName(node)
|
|
220
|
+
if className != "" && methodName != "" {
|
|
221
|
+
key := className + "." + methodName
|
|
222
|
+
if visited[key] == false {
|
|
223
|
+
visited[key] = true
|
|
224
|
+
metadata, err := nestiaSDKMetadataText(context, file, node)
|
|
225
|
+
site := nestiaSDKSite{
|
|
226
|
+
File: file,
|
|
227
|
+
FilePath: file.FileName(),
|
|
228
|
+
ClassName: className,
|
|
229
|
+
MethodName: methodName,
|
|
230
|
+
Method: node,
|
|
231
|
+
Metadata: metadata,
|
|
232
|
+
}
|
|
233
|
+
if err != nil {
|
|
234
|
+
*diagnostics = append(*diagnostics, nestiaSDKDiagnostic(site, err.Error()))
|
|
235
|
+
} else {
|
|
236
|
+
*sites = append(*sites, site)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
242
|
+
visitNestiaSDKNode(context, file, child, visited, sites, diagnostics)
|
|
243
|
+
return false
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
func nestiaSDKMetadataText(context *nestiaSDKContext, file *shimast.SourceFile, method *shimast.Node) (string, error) {
|
|
248
|
+
prog := context.prog
|
|
249
|
+
methodDecl := method.AsMethodDeclaration()
|
|
250
|
+
imports := context.imports(file)
|
|
251
|
+
doc := nestiaSDKMethodJSDoc(file, method)
|
|
252
|
+
parameters := []any{}
|
|
253
|
+
if methodDecl.Parameters != nil {
|
|
254
|
+
for index, param := range methodDecl.Parameters.Nodes {
|
|
255
|
+
typ := prog.Checker.GetTypeAtLocation(param)
|
|
256
|
+
name := nestiaSDKParameterName(param)
|
|
257
|
+
response := nestiaSDKResponse(context, imports, typ, nestiaSDKParameterTypeNode(param))
|
|
258
|
+
parameters = append(parameters, map[string]any{
|
|
259
|
+
"name": name,
|
|
260
|
+
"index": index,
|
|
261
|
+
"description": nestiaSDKNullableString(doc.Params[name]),
|
|
262
|
+
"jsDocTags": []any{},
|
|
263
|
+
"type": response["type"],
|
|
264
|
+
"imports": response["imports"],
|
|
265
|
+
"primitive": response["primitive"],
|
|
266
|
+
"resolved": response["resolved"],
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
returnType := nestiaCoreMethodReturnType(prog, method)
|
|
271
|
+
returnTypeNode := nestiaSDKMethodReturnTypeNode(method)
|
|
272
|
+
exceptions := nestiaSDKExceptionResponses(context, imports, method)
|
|
273
|
+
metadata := map[string]any{
|
|
274
|
+
"parameters": parameters,
|
|
275
|
+
"success": nestiaSDKResponse(context, imports, returnType, returnTypeNode),
|
|
276
|
+
"exceptions": exceptions,
|
|
277
|
+
"description": nestiaSDKNullableString(doc.Description),
|
|
278
|
+
"jsDocTags": doc.Tags,
|
|
279
|
+
}
|
|
280
|
+
return nestiaSDKMetadataLiteralText(metadata)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const nestiaSDKLiteralNull = "__NESTIA_LITERAL_NULL__"
|
|
284
|
+
|
|
285
|
+
func nestiaSDKMetadataLiteralText(metadata map[string]any) (string, error) {
|
|
286
|
+
data, err := json.Marshal(metadata)
|
|
287
|
+
if err != nil {
|
|
288
|
+
return "", err
|
|
289
|
+
}
|
|
290
|
+
text := string(data)
|
|
291
|
+
text = strings.ReplaceAll(text, `"`+nestiaSDKLiteralNull+`"`, "null")
|
|
292
|
+
return text, nil
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
type nestiaSDKJSDoc struct {
|
|
296
|
+
Description string
|
|
297
|
+
Tags []any
|
|
298
|
+
Params map[string]string
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
func nestiaSDKMethodJSDoc(file *shimast.SourceFile, method *shimast.Node) nestiaSDKJSDoc {
|
|
302
|
+
doc := nestiaSDKJSDoc{
|
|
303
|
+
Tags: []any{},
|
|
304
|
+
Params: map[string]string{},
|
|
305
|
+
}
|
|
306
|
+
source, ok := sourceFileText(file)
|
|
307
|
+
if ok == false || method == nil {
|
|
308
|
+
return doc
|
|
309
|
+
}
|
|
310
|
+
comment := nestiaSDKLeadingJSDoc(source, method)
|
|
311
|
+
if comment == "" {
|
|
312
|
+
return doc
|
|
313
|
+
}
|
|
314
|
+
description := []string{}
|
|
315
|
+
inTags := false
|
|
316
|
+
for _, line := range strings.Split(comment, "\n") {
|
|
317
|
+
text := strings.TrimSpace(line)
|
|
318
|
+
text = strings.TrimPrefix(text, "*")
|
|
319
|
+
text = strings.TrimSpace(text)
|
|
320
|
+
if text == "" {
|
|
321
|
+
if inTags == false && len(description) != 0 {
|
|
322
|
+
description = append(description, "")
|
|
323
|
+
}
|
|
324
|
+
continue
|
|
325
|
+
}
|
|
326
|
+
if strings.HasPrefix(text, "@") {
|
|
327
|
+
inTags = true
|
|
328
|
+
name, body := nestiaSDKParseJSDocTag(text)
|
|
329
|
+
doc.Tags = append(doc.Tags, nestiaSDKJSDocTag(name, body))
|
|
330
|
+
if name == "param" {
|
|
331
|
+
param, desc := nestiaSDKParseParamTag(body)
|
|
332
|
+
if param != "" {
|
|
333
|
+
doc.Params[param] = desc
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
continue
|
|
337
|
+
}
|
|
338
|
+
if inTags == false {
|
|
339
|
+
description = append(description, text)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
doc.Description = strings.TrimSpace(strings.Join(description, "\n"))
|
|
343
|
+
return doc
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
func nestiaSDKLeadingJSDoc(source string, method *shimast.Node) string {
|
|
347
|
+
positions := []int{method.Pos()}
|
|
348
|
+
if decorators := method.Decorators(); len(decorators) != 0 {
|
|
349
|
+
positions = append(positions, decorators[0].Pos())
|
|
350
|
+
}
|
|
351
|
+
for _, pos := range positions {
|
|
352
|
+
if pos < 0 || pos > len(source) {
|
|
353
|
+
continue
|
|
354
|
+
}
|
|
355
|
+
if comment := nestiaSDKJSDocAtOrBefore(source, pos); comment != "" {
|
|
356
|
+
return comment
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return ""
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
func nestiaSDKJSDocAtOrBefore(source string, pos int) string {
|
|
363
|
+
cursor := pos
|
|
364
|
+
for cursor < len(source) && (source[cursor] == ' ' || source[cursor] == '\t' || source[cursor] == '\r' || source[cursor] == '\n') {
|
|
365
|
+
cursor++
|
|
366
|
+
}
|
|
367
|
+
if strings.HasPrefix(source[cursor:], "/**") {
|
|
368
|
+
if end := strings.Index(source[cursor:], "*/"); end >= 0 {
|
|
369
|
+
return source[cursor+3 : cursor+end]
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
left := pos
|
|
373
|
+
for left > 0 && (source[left-1] == ' ' || source[left-1] == '\t' || source[left-1] == '\r' || source[left-1] == '\n') {
|
|
374
|
+
left--
|
|
375
|
+
}
|
|
376
|
+
if left < 2 || source[left-2:left] != "*/" {
|
|
377
|
+
return ""
|
|
378
|
+
}
|
|
379
|
+
start := strings.LastIndex(source[:left-2], "/**")
|
|
380
|
+
if start < 0 {
|
|
381
|
+
return ""
|
|
382
|
+
}
|
|
383
|
+
return source[start+3 : left-2]
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
func nestiaSDKParseJSDocTag(text string) (string, string) {
|
|
387
|
+
text = strings.TrimPrefix(text, "@")
|
|
388
|
+
parts := strings.Fields(text)
|
|
389
|
+
if len(parts) == 0 {
|
|
390
|
+
return "", ""
|
|
391
|
+
}
|
|
392
|
+
name := parts[0]
|
|
393
|
+
body := strings.TrimSpace(strings.TrimPrefix(text, name))
|
|
394
|
+
return name, body
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
func nestiaSDKParseParamTag(body string) (string, string) {
|
|
398
|
+
parts := strings.Fields(body)
|
|
399
|
+
if len(parts) == 0 {
|
|
400
|
+
return "", ""
|
|
401
|
+
}
|
|
402
|
+
param := parts[0]
|
|
403
|
+
desc := strings.TrimSpace(strings.TrimPrefix(body, param))
|
|
404
|
+
return param, desc
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
func nestiaSDKJSDocTag(name string, text string) map[string]any {
|
|
408
|
+
if text == "" {
|
|
409
|
+
return map[string]any{
|
|
410
|
+
"name": name,
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return map[string]any{
|
|
414
|
+
"name": name,
|
|
415
|
+
"text": []any{
|
|
416
|
+
map[string]any{
|
|
417
|
+
"text": text,
|
|
418
|
+
"kind": "text",
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
func nestiaSDKNullableString(value string) any {
|
|
425
|
+
value = strings.TrimSpace(value)
|
|
426
|
+
if value == "" {
|
|
427
|
+
return nestiaSDKLiteralNull
|
|
428
|
+
}
|
|
429
|
+
return value
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
func nestiaSDKExceptionResponses(
|
|
433
|
+
context *nestiaSDKContext,
|
|
434
|
+
imports []nestiaSDKImportInfo,
|
|
435
|
+
method *shimast.Node,
|
|
436
|
+
) []any {
|
|
437
|
+
prog := context.prog
|
|
438
|
+
responses := []any{}
|
|
439
|
+
for _, decorator := range method.Decorators() {
|
|
440
|
+
exception := nestiaSDKTypedExceptionInfo(prog, decorator)
|
|
441
|
+
if exception == nil {
|
|
442
|
+
continue
|
|
443
|
+
}
|
|
444
|
+
responses = append(responses, nestiaSDKResponse(context, imports, exception.Type, exception.Node))
|
|
445
|
+
}
|
|
446
|
+
return responses
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
type nestiaSDKTypedException struct {
|
|
450
|
+
Type *shimchecker.Type
|
|
451
|
+
Node *shimast.Node
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
func nestiaSDKTypedExceptionInfo(prog *driver.Program, decorator *shimast.Node) *nestiaSDKTypedException {
|
|
455
|
+
if decorator == nil || decorator.Kind != nestiaCoreKindDecorator {
|
|
456
|
+
return nil
|
|
457
|
+
}
|
|
458
|
+
expression := decorator.AsDecorator().Expression
|
|
459
|
+
if expression == nil || expression.Kind != shimast.KindCallExpression {
|
|
460
|
+
return nil
|
|
461
|
+
}
|
|
462
|
+
call := expression.AsCallExpression()
|
|
463
|
+
segments := nestiaCoreExpressionSegments(call.Expression)
|
|
464
|
+
if len(segments) == 0 || segments[len(segments)-1] != "TypedException" {
|
|
465
|
+
return nil
|
|
466
|
+
}
|
|
467
|
+
if isNestiaCoreCall(prog, expression) == false {
|
|
468
|
+
return nil
|
|
469
|
+
}
|
|
470
|
+
if call.TypeArguments == nil || len(call.TypeArguments.Nodes) != 1 {
|
|
471
|
+
return nil
|
|
472
|
+
}
|
|
473
|
+
node := call.TypeArguments.Nodes[0]
|
|
474
|
+
return &nestiaSDKTypedException{
|
|
475
|
+
Type: prog.Checker.GetTypeFromTypeNode(node),
|
|
476
|
+
Node: node,
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
type nestiaSDKImportInfo struct {
|
|
481
|
+
File string
|
|
482
|
+
Asterisk string
|
|
483
|
+
Default string
|
|
484
|
+
Elements []string
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
func nestiaSDKAnalyzeImports(file *shimast.SourceFile) []nestiaSDKImportInfo {
|
|
488
|
+
if file == nil || file.Statements == nil {
|
|
489
|
+
return nil
|
|
490
|
+
}
|
|
491
|
+
output := []nestiaSDKImportInfo{}
|
|
492
|
+
for _, stmt := range file.Statements.Nodes {
|
|
493
|
+
if stmt == nil || stmt.Kind != shimast.KindImportDeclaration {
|
|
494
|
+
continue
|
|
495
|
+
}
|
|
496
|
+
decl := stmt.AsImportDeclaration()
|
|
497
|
+
if decl == nil || decl.ImportClause == nil || decl.ModuleSpecifier == nil || decl.ModuleSpecifier.Kind != shimast.KindStringLiteral {
|
|
498
|
+
continue
|
|
499
|
+
}
|
|
500
|
+
clause := decl.ImportClause.AsImportClause()
|
|
501
|
+
if clause == nil {
|
|
502
|
+
continue
|
|
503
|
+
}
|
|
504
|
+
info := nestiaSDKImportInfo{
|
|
505
|
+
File: nestiaSDKNormalizeImportPath(file.FileName(), decl.ModuleSpecifier.Text()),
|
|
506
|
+
Elements: []string{},
|
|
507
|
+
}
|
|
508
|
+
if name := clause.Name(); name != nil {
|
|
509
|
+
info.Default = name.Text()
|
|
510
|
+
}
|
|
511
|
+
if clause.NamedBindings != nil {
|
|
512
|
+
if clause.NamedBindings.Kind == shimast.KindNamespaceImport {
|
|
513
|
+
if name := clause.NamedBindings.Name(); name != nil {
|
|
514
|
+
info.Asterisk = name.Text()
|
|
515
|
+
}
|
|
516
|
+
} else if clause.NamedBindings.Kind == shimast.KindNamedImports {
|
|
517
|
+
named := clause.NamedBindings.AsNamedImports()
|
|
518
|
+
if named != nil && named.Elements != nil {
|
|
519
|
+
for _, elem := range named.Elements.Nodes {
|
|
520
|
+
if elem == nil {
|
|
521
|
+
continue
|
|
522
|
+
}
|
|
523
|
+
spec := elem.AsImportSpecifier()
|
|
524
|
+
if spec == nil {
|
|
525
|
+
continue
|
|
526
|
+
}
|
|
527
|
+
if name := spec.Name(); name != nil {
|
|
528
|
+
info.Elements = append(info.Elements, name.Text())
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
output = append(output, info)
|
|
535
|
+
}
|
|
536
|
+
return output
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
func nestiaSDKNormalizeImportPath(fileName string, module string) string {
|
|
540
|
+
if strings.HasPrefix(module, ".") {
|
|
541
|
+
return filepath.ToSlash(filepath.Clean(filepath.Join(filepath.Dir(fileName), module)))
|
|
542
|
+
}
|
|
543
|
+
return filepath.ToSlash(filepath.Join("node_modules", module))
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
func nestiaSDKReflectImports(name string, imports []nestiaSDKImportInfo) []any {
|
|
547
|
+
prefixes := nestiaSDKTypePrefixes(name)
|
|
548
|
+
output := []any{}
|
|
549
|
+
seen := map[string]bool{}
|
|
550
|
+
for _, imp := range imports {
|
|
551
|
+
if nestiaSDKImportMatches(prefixes, imp) == false {
|
|
552
|
+
continue
|
|
553
|
+
}
|
|
554
|
+
item := nestiaSDKImportLiteral(imp, prefixes)
|
|
555
|
+
key := fmt.Sprintf("%v", item)
|
|
556
|
+
if seen[key] {
|
|
557
|
+
continue
|
|
558
|
+
}
|
|
559
|
+
seen[key] = true
|
|
560
|
+
output = append(output, item)
|
|
561
|
+
}
|
|
562
|
+
return output
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
func nestiaSDKTypePrefixes(name string) map[string]bool {
|
|
566
|
+
output := map[string]bool{}
|
|
567
|
+
re := regexp.MustCompile(`[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*`)
|
|
568
|
+
for _, match := range re.FindAllString(name, -1) {
|
|
569
|
+
prefix := strings.Split(match, ".")[0]
|
|
570
|
+
if nestiaSDKIsGlobalTypePrefix(prefix) == false {
|
|
571
|
+
output[prefix] = true
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
return output
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
func nestiaSDKIsGlobalTypePrefix(prefix string) bool {
|
|
578
|
+
switch prefix {
|
|
579
|
+
case "any", "unknown", "never", "void", "null", "undefined",
|
|
580
|
+
"string", "number", "boolean", "bigint", "symbol", "object",
|
|
581
|
+
"Array", "ReadonlyArray", "Promise", "Record", "Partial", "Pick", "Omit",
|
|
582
|
+
"Date", "File", "Blob", "Uint8Array", "ArrayBuffer", "Error":
|
|
583
|
+
return true
|
|
584
|
+
default:
|
|
585
|
+
return false
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
func nestiaSDKImportMatches(prefixes map[string]bool, imp nestiaSDKImportInfo) bool {
|
|
590
|
+
if imp.Default != "" && prefixes[imp.Default] {
|
|
591
|
+
return true
|
|
592
|
+
}
|
|
593
|
+
if imp.Asterisk != "" && prefixes[imp.Asterisk] {
|
|
594
|
+
return true
|
|
595
|
+
}
|
|
596
|
+
for _, elem := range imp.Elements {
|
|
597
|
+
if prefixes[elem] {
|
|
598
|
+
return true
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return false
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
func nestiaSDKImportLiteral(imp nestiaSDKImportInfo, prefixes map[string]bool) map[string]any {
|
|
605
|
+
elements := []string{}
|
|
606
|
+
for _, elem := range imp.Elements {
|
|
607
|
+
if prefixes[elem] {
|
|
608
|
+
elements = append(elements, elem)
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
asterisk := any(nestiaSDKLiteralNull)
|
|
612
|
+
if imp.Asterisk != "" && prefixes[imp.Asterisk] {
|
|
613
|
+
asterisk = imp.Asterisk
|
|
614
|
+
}
|
|
615
|
+
def := any(nestiaSDKLiteralNull)
|
|
616
|
+
if imp.Default != "" && prefixes[imp.Default] {
|
|
617
|
+
def = imp.Default
|
|
618
|
+
}
|
|
619
|
+
return map[string]any{
|
|
620
|
+
"file": imp.File,
|
|
621
|
+
"asterisk": asterisk,
|
|
622
|
+
"default": def,
|
|
623
|
+
"elements": elements,
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
func nestiaSDKResponse(
|
|
628
|
+
context *nestiaSDKContext,
|
|
629
|
+
imports []nestiaSDKImportInfo,
|
|
630
|
+
typ *shimchecker.Type,
|
|
631
|
+
typeNode *shimast.Node,
|
|
632
|
+
) map[string]any {
|
|
633
|
+
prog := context.prog
|
|
634
|
+
refType, refImports := nestiaSDKReflectType(prog, imports, typ, typeNode)
|
|
635
|
+
if refImports == nil {
|
|
636
|
+
refImports = []any{}
|
|
637
|
+
}
|
|
638
|
+
return map[string]any{
|
|
639
|
+
"type": refType,
|
|
640
|
+
"imports": refImports,
|
|
641
|
+
"primitive": nestiaSDKSchemaPipe(context, typ, typeNode, true),
|
|
642
|
+
"resolved": nestiaSDKSchemaPipe(context, typ, typeNode, false),
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
func nestiaSDKSchemaPipe(context *nestiaSDKContext, typ *shimchecker.Type, typeNode *shimast.Node, escape bool) any {
|
|
647
|
+
prog := context.prog
|
|
648
|
+
key := nestiaSDKSchemaCacheKey{
|
|
649
|
+
Type: typ,
|
|
650
|
+
Text: nestiaSDKTypeNodeText(typeNode),
|
|
651
|
+
Escape: escape,
|
|
652
|
+
}
|
|
653
|
+
if cached, ok := context.schemaCache[key]; ok {
|
|
654
|
+
context.schemaHits++
|
|
655
|
+
return cached
|
|
656
|
+
}
|
|
657
|
+
context.schemaMisses++
|
|
658
|
+
if nestiaSDKIsTypeGuardError(prog, typ, typeNode) {
|
|
659
|
+
value := nestiaSDKTypeGuardErrorSchemaPipe()
|
|
660
|
+
context.schemaCache[key] = value
|
|
661
|
+
return value
|
|
662
|
+
}
|
|
663
|
+
result := nativefactories.MetadataFactory.Analyze(nativefactories.MetadataFactory_IProps{
|
|
664
|
+
Checker: prog.Checker,
|
|
665
|
+
Options: nativefactories.MetadataFactory_IOptions{
|
|
666
|
+
Escape: escape,
|
|
667
|
+
Constant: true,
|
|
668
|
+
Absorb: true,
|
|
669
|
+
},
|
|
670
|
+
Components: context.collection,
|
|
671
|
+
Type: typ,
|
|
672
|
+
})
|
|
673
|
+
if result.Success == false {
|
|
674
|
+
errors := []any{}
|
|
675
|
+
for _, err := range result.Errors {
|
|
676
|
+
errors = append(errors, map[string]any{
|
|
677
|
+
"name": err.Name,
|
|
678
|
+
"accessor": nestiaSDKLiteralNull,
|
|
679
|
+
"messages": err.Messages,
|
|
680
|
+
})
|
|
681
|
+
}
|
|
682
|
+
failure := map[string]any{
|
|
683
|
+
"success": false,
|
|
684
|
+
"errors": errors,
|
|
685
|
+
}
|
|
686
|
+
context.schemaCache[key] = failure
|
|
687
|
+
return failure
|
|
688
|
+
}
|
|
689
|
+
nestiaSDKRestoreUnionOrder(typeNode, result.Data)
|
|
690
|
+
// Pre-bake the values that the legacy `@typia/core` 12.x `MetadataSchema`
|
|
691
|
+
// class exposed as runtime methods (.size(), .getName(), .empty()) and
|
|
692
|
+
// the OpenAPI 3.1 schema typia v13 only produces on the Go side. nestia
|
|
693
|
+
// reads them through the `packages/sdk/src/internal/legacy.ts` namespace
|
|
694
|
+
// utilities so no JS-side class wrapper or vendored package is needed.
|
|
695
|
+
metadataLiteral := nestiaSDKMetadataSchemaLiteral(result.Data.ToJSON()).(map[string]any)
|
|
696
|
+
metadataLiteral["size"] = result.Data.Size()
|
|
697
|
+
metadataLiteral["name"] = result.Data.GetName()
|
|
698
|
+
metadataLiteral["empty"] = result.Data.Empty()
|
|
699
|
+
// `JsonSchemasProgrammer.WriteSchemas` panics on metadata that has no
|
|
700
|
+
// JSON-schema representation (e.g. a `void` route return or a parameter
|
|
701
|
+
// whose only members are functions). The legacy reader on the JS side
|
|
702
|
+
// already treats a missing `jsonSchema` as "skip", so swallow the panic
|
|
703
|
+
// and omit the field — the sdk generator falls back to its own derived
|
|
704
|
+
// schema path. This must never mask a real bug, so re-raise anything we
|
|
705
|
+
// don't recognize as a transformer error from the typia runtime.
|
|
706
|
+
if baked := nestiaSDKTryBakeJsonSchema(result.Data); baked != nil {
|
|
707
|
+
metadataLiteral["jsonSchema"] = baked
|
|
708
|
+
}
|
|
709
|
+
value := map[string]any{
|
|
710
|
+
"success": true,
|
|
711
|
+
"data": map[string]any{
|
|
712
|
+
"components": nestiaSDKMetadataComponentsLiteral(nestiaSDKVisitedMetadataComponents(context.collection, result.Data)),
|
|
713
|
+
"metadata": metadataLiteral,
|
|
714
|
+
},
|
|
715
|
+
}
|
|
716
|
+
context.schemaCache[key] = value
|
|
717
|
+
return value
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// nestiaSDKTryBakeJsonSchema runs `JsonSchemasProgrammer.WriteSchemas` for a
|
|
721
|
+
// single metadata and returns the OpenAPI 3.1 schema literal. Returns nil
|
|
722
|
+
// when typia signals the metadata has no JSON-schema representation (e.g.
|
|
723
|
+
// `void` returns, function-only types) — the JS-side reader handles missing
|
|
724
|
+
// `jsonSchema` fields. Any other panic is re-raised so real bugs surface.
|
|
725
|
+
func nestiaSDKTryBakeJsonSchema(metadata *schemametadata.MetadataSchema) (baked map[string]any) {
|
|
726
|
+
defer func() {
|
|
727
|
+
if r := recover(); r != nil {
|
|
728
|
+
if _, ok := r.(*nativecontext.TransformerError); ok {
|
|
729
|
+
baked = nil
|
|
730
|
+
return
|
|
731
|
+
}
|
|
732
|
+
panic(r)
|
|
733
|
+
}
|
|
734
|
+
}()
|
|
735
|
+
collection := nativejson.JsonSchemasProgrammer.WriteSchemas(struct {
|
|
736
|
+
Version string
|
|
737
|
+
Metadatas []*schemametadata.MetadataSchema
|
|
738
|
+
}{
|
|
739
|
+
Version: "3.1",
|
|
740
|
+
Metadatas: []*schemametadata.MetadataSchema{metadata},
|
|
741
|
+
})
|
|
742
|
+
if len(collection.Schemas) == 0 {
|
|
743
|
+
return nil
|
|
744
|
+
}
|
|
745
|
+
// `iterate.OpenApi_IComponents` has no JSON tags on its Schemas field,
|
|
746
|
+
// so default Go marshaling would emit `"Schemas"` (capital). The JS
|
|
747
|
+
// side reads `components.schemas`, so flatten to a plain map here.
|
|
748
|
+
componentsLiteral := map[string]any{"schemas": map[string]any{}}
|
|
749
|
+
if collection.Components != nil && collection.Components.Schemas != nil {
|
|
750
|
+
schemasLiteral := map[string]any{}
|
|
751
|
+
for key, val := range collection.Components.Schemas {
|
|
752
|
+
schemasLiteral[key] = map[string]any(val)
|
|
753
|
+
}
|
|
754
|
+
componentsLiteral["schemas"] = schemasLiteral
|
|
755
|
+
}
|
|
756
|
+
return map[string]any{
|
|
757
|
+
"version": collection.Version,
|
|
758
|
+
"components": componentsLiteral,
|
|
759
|
+
"schema": map[string]any(collection.Schemas[0]),
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
func nestiaSDKRestoreUnionOrder(typeNode *shimast.Node, metadata *schemametadata.MetadataSchema) {
|
|
764
|
+
if typeNode == nil || metadata == nil || typeNode.Kind != shimast.KindUnionType {
|
|
765
|
+
return
|
|
766
|
+
}
|
|
767
|
+
types := typeNode.AsUnionTypeNode().Types
|
|
768
|
+
if types == nil || len(types.Nodes) == 0 {
|
|
769
|
+
return
|
|
770
|
+
}
|
|
771
|
+
order := map[string]int{}
|
|
772
|
+
for index, child := range types.Nodes {
|
|
773
|
+
for _, name := range nestiaSDKUnionTypeNames(child) {
|
|
774
|
+
if _, ok := order[name]; ok == false {
|
|
775
|
+
order[name] = index
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
if len(order) == 0 {
|
|
780
|
+
return
|
|
781
|
+
}
|
|
782
|
+
sort.SliceStable(metadata.Objects, func(i, j int) bool {
|
|
783
|
+
return nestiaSDKUnionRank(order, metadata.Objects[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Objects[j].Type.Name)
|
|
784
|
+
})
|
|
785
|
+
sort.SliceStable(metadata.Aliases, func(i, j int) bool {
|
|
786
|
+
return nestiaSDKUnionRank(order, metadata.Aliases[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Aliases[j].Type.Name)
|
|
787
|
+
})
|
|
788
|
+
sort.SliceStable(metadata.Arrays, func(i, j int) bool {
|
|
789
|
+
return nestiaSDKUnionRank(order, metadata.Arrays[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Arrays[j].Type.Name)
|
|
790
|
+
})
|
|
791
|
+
sort.SliceStable(metadata.Tuples, func(i, j int) bool {
|
|
792
|
+
return nestiaSDKUnionRank(order, metadata.Tuples[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Tuples[j].Type.Name)
|
|
793
|
+
})
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
func nestiaSDKVisitedMetadataComponents(
|
|
797
|
+
collection *schemametadata.MetadataCollection,
|
|
798
|
+
metadata *schemametadata.MetadataSchema,
|
|
799
|
+
) schemametadata.IMetadataComponents {
|
|
800
|
+
visited := map[string]bool{}
|
|
801
|
+
nestiaSDKVisitMetadataSchema(metadata, visited, map[*schemametadata.MetadataSchema]bool{})
|
|
802
|
+
filtered := schemametadata.IMetadataComponents{
|
|
803
|
+
Objects: []schemametadata.IMetadataSchema_IObjectType{},
|
|
804
|
+
Aliases: []schemametadata.IMetadataSchema_IAliasType{},
|
|
805
|
+
Arrays: []schemametadata.IMetadataSchema_IArrayType{},
|
|
806
|
+
Tuples: []schemametadata.IMetadataSchema_ITupleType{},
|
|
807
|
+
}
|
|
808
|
+
for _, obj := range collection.Objects() {
|
|
809
|
+
if obj != nil && visited[obj.Name] {
|
|
810
|
+
filtered.Objects = append(filtered.Objects, obj.ToJSON())
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
for _, alias := range collection.Aliases() {
|
|
814
|
+
if alias != nil && visited[alias.Name] {
|
|
815
|
+
filtered.Aliases = append(filtered.Aliases, alias.ToJSON())
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
for _, array := range collection.Arrays() {
|
|
819
|
+
if array != nil && visited[array.Name] {
|
|
820
|
+
filtered.Arrays = append(filtered.Arrays, array.ToJSON())
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
for _, tuple := range collection.Tuples() {
|
|
824
|
+
if tuple != nil && visited[tuple.Name] {
|
|
825
|
+
filtered.Tuples = append(filtered.Tuples, tuple.ToJSON())
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return filtered
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
func nestiaSDKVisitMetadataSchema(
|
|
832
|
+
metadata *schemametadata.MetadataSchema,
|
|
833
|
+
visited map[string]bool,
|
|
834
|
+
seen map[*schemametadata.MetadataSchema]bool,
|
|
835
|
+
) {
|
|
836
|
+
if metadata == nil || seen[metadata] {
|
|
837
|
+
return
|
|
838
|
+
}
|
|
839
|
+
seen[metadata] = true
|
|
840
|
+
if metadata.Escaped != nil {
|
|
841
|
+
nestiaSDKVisitMetadataSchema(metadata.Escaped.Original, visited, seen)
|
|
842
|
+
nestiaSDKVisitMetadataSchema(metadata.Escaped.Returns, visited, seen)
|
|
843
|
+
}
|
|
844
|
+
if metadata.Rest != nil {
|
|
845
|
+
nestiaSDKVisitMetadataSchema(metadata.Rest, visited, seen)
|
|
846
|
+
}
|
|
847
|
+
for _, alias := range metadata.Aliases {
|
|
848
|
+
if alias.Type != nil {
|
|
849
|
+
visited[alias.Type.Name] = true
|
|
850
|
+
nestiaSDKVisitMetadataSchema(alias.Type.Value, visited, seen)
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
for _, array := range metadata.Arrays {
|
|
854
|
+
if array.Type != nil {
|
|
855
|
+
visited[array.Type.Name] = true
|
|
856
|
+
nestiaSDKVisitMetadataSchema(array.Type.Value, visited, seen)
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
for _, tuple := range metadata.Tuples {
|
|
860
|
+
if tuple.Type != nil {
|
|
861
|
+
visited[tuple.Type.Name] = true
|
|
862
|
+
for _, elem := range tuple.Type.Elements {
|
|
863
|
+
nestiaSDKVisitMetadataSchema(elem, visited, seen)
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
for _, object := range metadata.Objects {
|
|
868
|
+
if object.Type != nil {
|
|
869
|
+
visited[object.Type.Name] = true
|
|
870
|
+
for _, prop := range object.Type.Properties {
|
|
871
|
+
if prop == nil {
|
|
872
|
+
continue
|
|
873
|
+
}
|
|
874
|
+
nestiaSDKVisitMetadataSchema(prop.Key, visited, seen)
|
|
875
|
+
nestiaSDKVisitMetadataSchema(prop.Value, visited, seen)
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
for _, set := range metadata.Sets {
|
|
880
|
+
nestiaSDKVisitMetadataSchema(set.Value, visited, seen)
|
|
881
|
+
}
|
|
882
|
+
for _, item := range metadata.Maps {
|
|
883
|
+
nestiaSDKVisitMetadataSchema(item.Key, visited, seen)
|
|
884
|
+
nestiaSDKVisitMetadataSchema(item.Value, visited, seen)
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
func nestiaSDKUnionTypeNames(node *shimast.Node) []string {
|
|
889
|
+
if node == nil {
|
|
890
|
+
return nil
|
|
891
|
+
}
|
|
892
|
+
names := []string{}
|
|
893
|
+
add := func(name string) {
|
|
894
|
+
name = strings.TrimSpace(name)
|
|
895
|
+
if name != "" {
|
|
896
|
+
names = append(names, name)
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
add(nestiaSDKTypeNodeText(node))
|
|
900
|
+
if node.Kind == shimast.KindTypeReference {
|
|
901
|
+
add(nestiaSDKEntityNameText(node.AsTypeReferenceNode().TypeName))
|
|
902
|
+
}
|
|
903
|
+
return names
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
func nestiaSDKUnionRank(order map[string]int, name string) int {
|
|
907
|
+
if index, ok := order[name]; ok {
|
|
908
|
+
return index
|
|
909
|
+
}
|
|
910
|
+
return len(order)
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
func nestiaSDKIsTypeGuardError(prog *driver.Program, typ *shimchecker.Type, typeNode *shimast.Node) bool {
|
|
914
|
+
if typeNode != nil {
|
|
915
|
+
name := nestiaSDKTypeNodeText(typeNode)
|
|
916
|
+
if name == "TypeGuardError" || strings.HasPrefix(name, "TypeGuardError<") {
|
|
917
|
+
return true
|
|
918
|
+
}
|
|
919
|
+
if typeNode.Kind == shimast.KindTypeReference {
|
|
920
|
+
ref := typeNode.AsTypeReferenceNode()
|
|
921
|
+
name = nestiaSDKEntityNameText(ref.TypeName)
|
|
922
|
+
if name == "TypeGuardError" {
|
|
923
|
+
return true
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
if prog != nil && prog.Checker != nil && typ != nil {
|
|
928
|
+
name := prog.Checker.TypeToString(typ)
|
|
929
|
+
return name == "TypeGuardError" || strings.HasPrefix(name, "TypeGuardError<")
|
|
930
|
+
}
|
|
931
|
+
return false
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
func nestiaSDKTypeGuardErrorSchemaPipe() any {
|
|
935
|
+
// Synthetic metadata for `TypeGuardError` exception responses — does not
|
|
936
|
+
// flow through `nestiaSDKSchemaPipe`, so the pre-baked fields legacy.ts
|
|
937
|
+
// reads (size/name/empty/jsonSchema) have to be filled by hand.
|
|
938
|
+
metadata := nestiaSDKObjectReferenceSchema("TypeGuardErrorany")
|
|
939
|
+
metadata["size"] = 1
|
|
940
|
+
metadata["name"] = "TypeGuardErrorany"
|
|
941
|
+
metadata["empty"] = false
|
|
942
|
+
metadata["jsonSchema"] = map[string]any{
|
|
943
|
+
"version": "3.1",
|
|
944
|
+
"components": map[string]any{
|
|
945
|
+
"schemas": map[string]any{
|
|
946
|
+
"TypeGuardErrorany": map[string]any{
|
|
947
|
+
"type": "object",
|
|
948
|
+
"additionalProperties": false,
|
|
949
|
+
"required": []any{"name", "method", "expected", "value"},
|
|
950
|
+
"properties": map[string]any{
|
|
951
|
+
"name": map[string]any{"type": "string"},
|
|
952
|
+
"method": map[string]any{"type": "string"},
|
|
953
|
+
"path": map[string]any{"type": "string"},
|
|
954
|
+
"expected": map[string]any{"type": "string"},
|
|
955
|
+
"value": map[string]any{},
|
|
956
|
+
"description": map[string]any{"type": "string"},
|
|
957
|
+
"message": map[string]any{"type": "string"},
|
|
958
|
+
},
|
|
959
|
+
},
|
|
960
|
+
},
|
|
961
|
+
},
|
|
962
|
+
"schema": map[string]any{
|
|
963
|
+
"$ref": "#/components/schemas/TypeGuardErrorany",
|
|
964
|
+
},
|
|
965
|
+
}
|
|
966
|
+
return map[string]any{
|
|
967
|
+
"success": true,
|
|
968
|
+
"data": map[string]any{
|
|
969
|
+
"components": map[string]any{
|
|
970
|
+
"aliases": []any{},
|
|
971
|
+
"arrays": []any{},
|
|
972
|
+
"objects": []any{
|
|
973
|
+
map[string]any{
|
|
974
|
+
"description": nil,
|
|
975
|
+
"index": 0,
|
|
976
|
+
"jsDocTags": []any{},
|
|
977
|
+
"name": "TypeGuardErrorany",
|
|
978
|
+
"nullables": []any{false},
|
|
979
|
+
"recursive": false,
|
|
980
|
+
"properties": []any{
|
|
981
|
+
nestiaSDKTypeGuardErrorProperty("name", nestiaSDKAtomicSchema("string", true)),
|
|
982
|
+
nestiaSDKTypeGuardErrorProperty("method", nestiaSDKAtomicSchema("string", true)),
|
|
983
|
+
nestiaSDKTypeGuardErrorProperty("path", nestiaSDKAtomicSchema("string", false)),
|
|
984
|
+
nestiaSDKTypeGuardErrorProperty("expected", nestiaSDKAtomicSchema("string", true)),
|
|
985
|
+
nestiaSDKTypeGuardErrorProperty("value", nestiaSDKAnySchema(true)),
|
|
986
|
+
nestiaSDKTypeGuardErrorProperty("description", nestiaSDKAtomicSchema("string", false)),
|
|
987
|
+
nestiaSDKTypeGuardErrorProperty("message", nestiaSDKAtomicSchema("string", false)),
|
|
988
|
+
},
|
|
989
|
+
},
|
|
990
|
+
},
|
|
991
|
+
"tuples": []any{},
|
|
992
|
+
},
|
|
993
|
+
"metadata": metadata,
|
|
994
|
+
},
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
func nestiaSDKTypeGuardErrorProperty(key string, value map[string]any) map[string]any {
|
|
999
|
+
return map[string]any{
|
|
1000
|
+
"description": nil,
|
|
1001
|
+
"jsDocTags": []any{},
|
|
1002
|
+
"key": nestiaSDKStringConstantSchema(key),
|
|
1003
|
+
"mutability": nil,
|
|
1004
|
+
"value": value,
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
func nestiaSDKStringConstantSchema(value string) map[string]any {
|
|
1009
|
+
schema := nestiaSDKBaseSchema(true)
|
|
1010
|
+
schema["constants"] = []any{
|
|
1011
|
+
map[string]any{
|
|
1012
|
+
"type": "string",
|
|
1013
|
+
"values": []any{
|
|
1014
|
+
map[string]any{
|
|
1015
|
+
"description": nil,
|
|
1016
|
+
"jsDocTags": []any{},
|
|
1017
|
+
"tags": []any{},
|
|
1018
|
+
"value": value,
|
|
1019
|
+
},
|
|
1020
|
+
},
|
|
1021
|
+
},
|
|
1022
|
+
}
|
|
1023
|
+
return schema
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
func nestiaSDKAtomicSchema(kind string, required bool) map[string]any {
|
|
1027
|
+
schema := nestiaSDKBaseSchema(required)
|
|
1028
|
+
schema["atomics"] = []any{
|
|
1029
|
+
map[string]any{
|
|
1030
|
+
"type": kind,
|
|
1031
|
+
"tags": []any{},
|
|
1032
|
+
},
|
|
1033
|
+
}
|
|
1034
|
+
return schema
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
func nestiaSDKAnySchema(required bool) map[string]any {
|
|
1038
|
+
schema := nestiaSDKBaseSchema(required)
|
|
1039
|
+
schema["any"] = true
|
|
1040
|
+
return schema
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
func nestiaSDKObjectReferenceSchema(name string) map[string]any {
|
|
1044
|
+
schema := nestiaSDKBaseSchema(true)
|
|
1045
|
+
schema["objects"] = []any{
|
|
1046
|
+
map[string]any{
|
|
1047
|
+
"name": name,
|
|
1048
|
+
"tags": []any{},
|
|
1049
|
+
},
|
|
1050
|
+
}
|
|
1051
|
+
return schema
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
func nestiaSDKBaseSchema(required bool) map[string]any {
|
|
1055
|
+
return map[string]any{
|
|
1056
|
+
"aliases": []any{},
|
|
1057
|
+
"any": false,
|
|
1058
|
+
"arrays": []any{},
|
|
1059
|
+
"atomics": []any{},
|
|
1060
|
+
"constants": []any{},
|
|
1061
|
+
"escaped": nil,
|
|
1062
|
+
"functions": []any{},
|
|
1063
|
+
"maps": []any{},
|
|
1064
|
+
"natives": []any{},
|
|
1065
|
+
"nullable": false,
|
|
1066
|
+
"objects": []any{},
|
|
1067
|
+
"optional": !required,
|
|
1068
|
+
"required": required,
|
|
1069
|
+
"rest": nil,
|
|
1070
|
+
"sets": []any{},
|
|
1071
|
+
"templates": []any{},
|
|
1072
|
+
"tuples": []any{},
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
func nestiaSDKReflectType(
|
|
1077
|
+
prog *driver.Program,
|
|
1078
|
+
imports []nestiaSDKImportInfo,
|
|
1079
|
+
typ *shimchecker.Type,
|
|
1080
|
+
typeNode *shimast.Node,
|
|
1081
|
+
) (map[string]any, []any) {
|
|
1082
|
+
if typeNode == nil {
|
|
1083
|
+
return map[string]any{"name": "__type"}, []any{}
|
|
1084
|
+
}
|
|
1085
|
+
if typeNode != nil {
|
|
1086
|
+
if ref, refs, ok := nestiaSDKReflectTypeNode(prog, imports, typeNode); ok {
|
|
1087
|
+
return ref, refs
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
name := ""
|
|
1091
|
+
if typeNode != nil {
|
|
1092
|
+
name = nestiaSDKTypeNodeText(typeNode)
|
|
1093
|
+
}
|
|
1094
|
+
if name == "" {
|
|
1095
|
+
name = "any"
|
|
1096
|
+
}
|
|
1097
|
+
if name == "any" && prog != nil && prog.Checker != nil && typ != nil {
|
|
1098
|
+
name = prog.Checker.TypeToString(typ)
|
|
1099
|
+
}
|
|
1100
|
+
return map[string]any{"name": name}, nestiaSDKReflectImports(name, imports)
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
func nestiaSDKReflectTypeNode(
|
|
1104
|
+
prog *driver.Program,
|
|
1105
|
+
imports []nestiaSDKImportInfo,
|
|
1106
|
+
node *shimast.Node,
|
|
1107
|
+
) (map[string]any, []any, bool) {
|
|
1108
|
+
if node == nil {
|
|
1109
|
+
return nil, nil, false
|
|
1110
|
+
}
|
|
1111
|
+
switch node.Kind {
|
|
1112
|
+
case shimast.KindIntersectionType:
|
|
1113
|
+
ref, refs := nestiaSDKReflectJoinedTypeNode(prog, imports, node.AsIntersectionTypeNode().Types, " & ")
|
|
1114
|
+
return ref, refs, true
|
|
1115
|
+
case shimast.KindUnionType:
|
|
1116
|
+
ref, refs := nestiaSDKReflectJoinedTypeNode(prog, imports, node.AsUnionTypeNode().Types, " | ")
|
|
1117
|
+
return ref, refs, true
|
|
1118
|
+
case shimast.KindArrayType:
|
|
1119
|
+
element, refs, ok := nestiaSDKReflectTypeNode(prog, imports, node.AsArrayTypeNode().ElementType)
|
|
1120
|
+
if ok == false {
|
|
1121
|
+
element = map[string]any{"name": nestiaSDKTypeNodeText(node.AsArrayTypeNode().ElementType)}
|
|
1122
|
+
refs = nestiaSDKReflectImports(element["name"].(string), imports)
|
|
1123
|
+
}
|
|
1124
|
+
return map[string]any{
|
|
1125
|
+
"name": "Array",
|
|
1126
|
+
"typeArguments": []any{element},
|
|
1127
|
+
}, refs, true
|
|
1128
|
+
case shimast.KindParenthesizedType:
|
|
1129
|
+
child, refs, ok := nestiaSDKReflectTypeNode(prog, imports, node.AsParenthesizedTypeNode().Type)
|
|
1130
|
+
if ok == false {
|
|
1131
|
+
child = map[string]any{"name": nestiaSDKTypeNodeText(node.AsParenthesizedTypeNode().Type)}
|
|
1132
|
+
refs = nestiaSDKReflectImports(child["name"].(string), imports)
|
|
1133
|
+
}
|
|
1134
|
+
name, _ := child["name"].(string)
|
|
1135
|
+
return map[string]any{"name": "(" + name + ")"}, refs, true
|
|
1136
|
+
case shimast.KindTypeOperator:
|
|
1137
|
+
operator := node.AsTypeOperatorNode()
|
|
1138
|
+
prefix := nestiaSDKTypeOperatorPrefix(node, operator.Type)
|
|
1139
|
+
if prefix == "" {
|
|
1140
|
+
return nil, nil, false
|
|
1141
|
+
}
|
|
1142
|
+
child, refs, ok := nestiaSDKReflectTypeNode(prog, imports, operator.Type)
|
|
1143
|
+
if ok == false {
|
|
1144
|
+
child = map[string]any{"name": nestiaSDKTypeNodeText(operator.Type)}
|
|
1145
|
+
refs = nestiaSDKReflectImports(child["name"].(string), imports)
|
|
1146
|
+
}
|
|
1147
|
+
name, _ := child["name"].(string)
|
|
1148
|
+
return map[string]any{"name": prefix + " " + name}, refs, true
|
|
1149
|
+
case shimast.KindTypeQuery:
|
|
1150
|
+
return nil, nil, false
|
|
1151
|
+
case shimast.KindTypeReference:
|
|
1152
|
+
ref := node.AsTypeReferenceNode()
|
|
1153
|
+
name := nestiaSDKEntityNameText(ref.TypeName)
|
|
1154
|
+
rootRefs := nestiaSDKReflectImports(name, imports)
|
|
1155
|
+
if len(rootRefs) == 0 && name != "Promise" {
|
|
1156
|
+
rootRefs = nestiaSDKReflectTypeReferenceSymbolImport(prog, ref.TypeName, name)
|
|
1157
|
+
}
|
|
1158
|
+
if ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) != 0 {
|
|
1159
|
+
if name == "Promise" && len(ref.TypeArguments.Nodes) == 1 {
|
|
1160
|
+
return nestiaSDKReflectTypeNode(prog, imports, ref.TypeArguments.Nodes[0])
|
|
1161
|
+
}
|
|
1162
|
+
args := make([]any, 0, len(ref.TypeArguments.Nodes))
|
|
1163
|
+
groups := [][]any{rootRefs}
|
|
1164
|
+
for _, child := range ref.TypeArguments.Nodes {
|
|
1165
|
+
arg, refs, ok := nestiaSDKReflectTypeNode(prog, imports, child)
|
|
1166
|
+
if ok == false {
|
|
1167
|
+
text := nestiaSDKTypeNodeText(child)
|
|
1168
|
+
arg = map[string]any{"name": text}
|
|
1169
|
+
refs = nestiaSDKReflectImports(text, imports)
|
|
1170
|
+
}
|
|
1171
|
+
args = append(args, arg)
|
|
1172
|
+
groups = append(groups, refs)
|
|
1173
|
+
}
|
|
1174
|
+
return map[string]any{
|
|
1175
|
+
"name": name,
|
|
1176
|
+
"typeArguments": args,
|
|
1177
|
+
}, nestiaSDKMergeImportLiterals(groups...), true
|
|
1178
|
+
}
|
|
1179
|
+
return map[string]any{"name": name}, rootRefs, true
|
|
1180
|
+
default:
|
|
1181
|
+
name := nestiaSDKTypeNodeText(node)
|
|
1182
|
+
if name == "" {
|
|
1183
|
+
return nil, nil, false
|
|
1184
|
+
}
|
|
1185
|
+
return map[string]any{"name": name}, nestiaSDKReflectImports(name, imports), true
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
func nestiaSDKReflectTypeReferenceSymbolImport(prog *driver.Program, node *shimast.Node, name string) []any {
|
|
1190
|
+
if prog == nil || prog.Checker == nil || node == nil {
|
|
1191
|
+
return nil
|
|
1192
|
+
}
|
|
1193
|
+
prefix := strings.Split(name, ".")[0]
|
|
1194
|
+
if nestiaSDKIsGlobalTypePrefix(prefix) {
|
|
1195
|
+
return nil
|
|
1196
|
+
}
|
|
1197
|
+
symbol := prog.Checker.GetSymbolAtLocation(node)
|
|
1198
|
+
if symbol == nil {
|
|
1199
|
+
typ := prog.Checker.GetTypeFromTypeNode(node)
|
|
1200
|
+
if typ != nil {
|
|
1201
|
+
symbol = typ.Symbol()
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
if symbol == nil || len(symbol.Declarations) == 0 {
|
|
1205
|
+
return nil
|
|
1206
|
+
}
|
|
1207
|
+
sourceFile := shimast.GetSourceFileOfNode(symbol.Declarations[0])
|
|
1208
|
+
if sourceFile == nil {
|
|
1209
|
+
return nil
|
|
1210
|
+
}
|
|
1211
|
+
file := filepath.ToSlash(sourceFile.FileName())
|
|
1212
|
+
if strings.Contains(file, "/typescript/lib/") {
|
|
1213
|
+
return nil
|
|
1214
|
+
}
|
|
1215
|
+
return []any{
|
|
1216
|
+
map[string]any{
|
|
1217
|
+
"file": file,
|
|
1218
|
+
"asterisk": nestiaSDKLiteralNull,
|
|
1219
|
+
"default": nestiaSDKLiteralNull,
|
|
1220
|
+
"elements": []string{prefix},
|
|
1221
|
+
},
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
func nestiaSDKReflectJoinedTypeNode(
|
|
1226
|
+
prog *driver.Program,
|
|
1227
|
+
imports []nestiaSDKImportInfo,
|
|
1228
|
+
types *shimast.TypeList,
|
|
1229
|
+
joiner string,
|
|
1230
|
+
) (map[string]any, []any) {
|
|
1231
|
+
if types == nil {
|
|
1232
|
+
return map[string]any{"name": ""}, nil
|
|
1233
|
+
}
|
|
1234
|
+
names := make([]string, 0, len(types.Nodes))
|
|
1235
|
+
groups := [][]any{}
|
|
1236
|
+
for _, child := range types.Nodes {
|
|
1237
|
+
ref, refs, ok := nestiaSDKReflectTypeNode(prog, imports, child)
|
|
1238
|
+
if ok == false {
|
|
1239
|
+
text := nestiaSDKTypeNodeText(child)
|
|
1240
|
+
names = append(names, text)
|
|
1241
|
+
groups = append(groups, nestiaSDKReflectImports(text, imports))
|
|
1242
|
+
} else {
|
|
1243
|
+
names = append(names, nestiaSDKReflectTypeText(ref))
|
|
1244
|
+
groups = append(groups, refs)
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
return map[string]any{"name": strings.Join(names, joiner)}, nestiaSDKMergeImportLiterals(groups...)
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
func nestiaSDKReflectTypeText(ref map[string]any) string {
|
|
1251
|
+
name, _ := ref["name"].(string)
|
|
1252
|
+
args, ok := ref["typeArguments"].([]any)
|
|
1253
|
+
if ok == false || len(args) == 0 {
|
|
1254
|
+
return name
|
|
1255
|
+
}
|
|
1256
|
+
texts := make([]string, 0, len(args))
|
|
1257
|
+
for _, arg := range args {
|
|
1258
|
+
child, ok := arg.(map[string]any)
|
|
1259
|
+
if ok == false {
|
|
1260
|
+
continue
|
|
1261
|
+
}
|
|
1262
|
+
texts = append(texts, nestiaSDKReflectTypeText(child))
|
|
1263
|
+
}
|
|
1264
|
+
return name + "<" + strings.Join(texts, ", ") + ">"
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
func nestiaSDKMergeImportLiterals(groups ...[]any) []any {
|
|
1268
|
+
output := []any{}
|
|
1269
|
+
seen := map[string]bool{}
|
|
1270
|
+
for _, group := range groups {
|
|
1271
|
+
for _, item := range group {
|
|
1272
|
+
key := fmt.Sprintf("%#v", item)
|
|
1273
|
+
if seen[key] {
|
|
1274
|
+
continue
|
|
1275
|
+
}
|
|
1276
|
+
seen[key] = true
|
|
1277
|
+
output = append(output, item)
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
return output
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
func nestiaSDKTypeOperatorPrefix(node *shimast.Node, operand *shimast.Node) string {
|
|
1284
|
+
text := nestiaSDKTypeNodeText(node)
|
|
1285
|
+
child := nestiaSDKTypeNodeText(operand)
|
|
1286
|
+
prefix := strings.TrimSpace(strings.TrimSuffix(text, child))
|
|
1287
|
+
switch prefix {
|
|
1288
|
+
case "keyof", "unique", "readonly":
|
|
1289
|
+
return prefix
|
|
1290
|
+
}
|
|
1291
|
+
return ""
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
func nestiaSDKParentClassName(node *shimast.Node) string {
|
|
1295
|
+
for parent := node.Parent; parent != nil; parent = parent.Parent {
|
|
1296
|
+
if parent.Kind == shimast.KindClassDeclaration {
|
|
1297
|
+
if name := parent.Name(); name != nil {
|
|
1298
|
+
return name.Text()
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
return ""
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
func nestiaSDKMethodName(node *shimast.Node) string {
|
|
1306
|
+
if node == nil || node.Name() == nil {
|
|
1307
|
+
return ""
|
|
1308
|
+
}
|
|
1309
|
+
return strings.Trim(node.Name().Text(), "\"'")
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
func nestiaSDKParameterName(node *shimast.Node) string {
|
|
1313
|
+
if node == nil || node.Name() == nil {
|
|
1314
|
+
return ""
|
|
1315
|
+
}
|
|
1316
|
+
return strings.Trim(node.Name().Text(), "\"'")
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
func nestiaSDKParameterTypeName(prog *driver.Program, node *shimast.Node, typ *shimchecker.Type) string {
|
|
1320
|
+
if node != nil && node.AsParameterDeclaration() != nil {
|
|
1321
|
+
if typeNode := node.AsParameterDeclaration().Type; typeNode != nil {
|
|
1322
|
+
return nestiaSDKTypeNodeText(typeNode)
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
return nestiaSDKTypeNameFromChecker(prog, typ)
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
func nestiaSDKParameterTypeNode(node *shimast.Node) *shimast.Node {
|
|
1329
|
+
if node != nil && node.AsParameterDeclaration() != nil {
|
|
1330
|
+
return node.AsParameterDeclaration().Type
|
|
1331
|
+
}
|
|
1332
|
+
return nil
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
func nestiaSDKMethodReturnTypeName(prog *driver.Program, method *shimast.Node, typ *shimchecker.Type) string {
|
|
1336
|
+
if method != nil && method.FunctionLikeData() != nil {
|
|
1337
|
+
if typeNode := method.FunctionLikeData().Type; typeNode != nil {
|
|
1338
|
+
return nestiaSDKReturnTypeNodeText(typeNode)
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
return nestiaSDKTypeNameFromChecker(prog, typ)
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
func nestiaSDKMethodReturnTypeNode(method *shimast.Node) *shimast.Node {
|
|
1345
|
+
if method != nil && method.FunctionLikeData() != nil {
|
|
1346
|
+
if typeNode := method.FunctionLikeData().Type; typeNode != nil {
|
|
1347
|
+
return nestiaSDKReturnTypeNode(typeNode)
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return nil
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
func nestiaSDKReturnTypeNode(node *shimast.Node) *shimast.Node {
|
|
1354
|
+
if node != nil && node.Kind == shimast.KindTypeReference {
|
|
1355
|
+
ref := node.AsTypeReferenceNode()
|
|
1356
|
+
if ref != nil && ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) == 1 && nestiaSDKEntityNameText(ref.TypeName) == "Promise" {
|
|
1357
|
+
return ref.TypeArguments.Nodes[0]
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
return node
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
func nestiaSDKEntityNameText(node *shimast.Node) string {
|
|
1364
|
+
return nestiaSDKTypeNodeText(node)
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
func nestiaSDKReturnTypeNodeText(node *shimast.Node) string {
|
|
1368
|
+
text := nestiaSDKTypeNodeText(node)
|
|
1369
|
+
if node != nil && node.Kind == shimast.KindTypeReference {
|
|
1370
|
+
ref := node.AsTypeReferenceNode()
|
|
1371
|
+
if ref != nil && ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) == 1 && strings.HasPrefix(strings.TrimSpace(text), "Promise<") {
|
|
1372
|
+
return nestiaSDKTypeNodeText(ref.TypeArguments.Nodes[0])
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
return text
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
func nestiaSDKTypeNodeText(node *shimast.Node) string {
|
|
1379
|
+
if node == nil {
|
|
1380
|
+
return ""
|
|
1381
|
+
}
|
|
1382
|
+
file := shimast.GetSourceFileOfNode(node)
|
|
1383
|
+
source, ok := sourceFileText(file)
|
|
1384
|
+
if ok == false {
|
|
1385
|
+
return ""
|
|
1386
|
+
}
|
|
1387
|
+
start, end := node.Pos(), node.End()
|
|
1388
|
+
if start < 0 || end > len(source) || start >= end {
|
|
1389
|
+
return ""
|
|
1390
|
+
}
|
|
1391
|
+
return strings.TrimSpace(source[start:end])
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
func nestiaSDKTypeNameFromChecker(prog *driver.Program, typ *shimchecker.Type) string {
|
|
1395
|
+
if prog != nil && prog.Checker != nil && typ != nil {
|
|
1396
|
+
return prog.Checker.TypeToString(typ)
|
|
1397
|
+
}
|
|
1398
|
+
return "any"
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
func insertSDKOperationMetadataDecorators(text string, sites []nestiaSDKSite) string {
|
|
1402
|
+
if len(sites) == 0 {
|
|
1403
|
+
return text
|
|
1404
|
+
}
|
|
1405
|
+
cursor := 0
|
|
1406
|
+
out := text
|
|
1407
|
+
for _, site := range sites {
|
|
1408
|
+
next, ok := insertSDKOperationMetadataDecorator(out, site, cursor)
|
|
1409
|
+
if ok {
|
|
1410
|
+
out = next.text
|
|
1411
|
+
cursor = next.cursor
|
|
1412
|
+
continue
|
|
1413
|
+
}
|
|
1414
|
+
next, ok = insertSDKOperationMetadataDecorator(out, site, 0)
|
|
1415
|
+
if ok {
|
|
1416
|
+
out = next.text
|
|
1417
|
+
cursor = next.cursor
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
return out
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
type sdkOperationMetadataInsertResult struct {
|
|
1424
|
+
text string
|
|
1425
|
+
cursor int
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
func insertSDKOperationMetadataDecorator(text string, site nestiaSDKSite, offset int) (sdkOperationMetadataInsertResult, bool) {
|
|
1429
|
+
if offset < 0 || offset > len(text) {
|
|
1430
|
+
offset = 0
|
|
1431
|
+
}
|
|
1432
|
+
needle := "], " + site.ClassName + ".prototype, \"" + site.MethodName + "\","
|
|
1433
|
+
relative := strings.Index(text[offset:], needle)
|
|
1434
|
+
idx := -1
|
|
1435
|
+
if relative >= 0 {
|
|
1436
|
+
idx = offset + relative
|
|
1437
|
+
}
|
|
1438
|
+
if idx < 0 {
|
|
1439
|
+
return sdkOperationMetadataInsertResult{text: text, cursor: offset}, false
|
|
1440
|
+
}
|
|
1441
|
+
head := strings.LastIndex(text[:idx], "__decorate([")
|
|
1442
|
+
if head < 0 {
|
|
1443
|
+
return sdkOperationMetadataInsertResult{text: text, cursor: offset}, false
|
|
1444
|
+
}
|
|
1445
|
+
insert := head + len("__decorate([")
|
|
1446
|
+
decorator := "\n __OperationMetadata.OperationMetadata(" + site.Metadata + "),"
|
|
1447
|
+
next := text[:insert] + decorator + text[insert:]
|
|
1448
|
+
return sdkOperationMetadataInsertResult{
|
|
1449
|
+
text: next,
|
|
1450
|
+
cursor: idx + len(decorator) + len(needle),
|
|
1451
|
+
}, true
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
func injectSDKOperationMetadataImport(text string) string {
|
|
1455
|
+
if strings.Contains(text, "__OperationMetadata.OperationMetadata") == false ||
|
|
1456
|
+
strings.Contains(text, "const __OperationMetadata = require(\"@nestia/sdk\")") ||
|
|
1457
|
+
strings.Contains(text, "import * as __OperationMetadata from \"@nestia/sdk\"") {
|
|
1458
|
+
return text
|
|
1459
|
+
}
|
|
1460
|
+
esModule := sdkIsESModuleOutput(text)
|
|
1461
|
+
var stmt string
|
|
1462
|
+
if esModule {
|
|
1463
|
+
stmt = "import * as __OperationMetadata from \"@nestia/sdk\";\n"
|
|
1464
|
+
} else {
|
|
1465
|
+
stmt = "const __OperationMetadata = require(\"@nestia/sdk\");\n"
|
|
1466
|
+
}
|
|
1467
|
+
index := sdkRuntimeImportInsertionIndex(text, esModule)
|
|
1468
|
+
return text[:index] + stmt + text[index:]
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
func sdkIsESModuleOutput(text string) bool {
|
|
1472
|
+
return regexp.MustCompile(`(?m)^(import\s|import\{|import\*|export\s)`).MatchString(text)
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
func sdkRuntimeImportInsertionIndex(text string, esModule bool) int {
|
|
1476
|
+
index := 0
|
|
1477
|
+
if strings.HasPrefix(text, "#!") {
|
|
1478
|
+
if next := strings.IndexByte(text, '\n'); next >= 0 {
|
|
1479
|
+
index = next + 1
|
|
1480
|
+
} else {
|
|
1481
|
+
return len(text)
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
if esModule {
|
|
1485
|
+
return index
|
|
1486
|
+
}
|
|
1487
|
+
for {
|
|
1488
|
+
next := sdkConsumeRuntimeImportPrefix(text[index:])
|
|
1489
|
+
if next == 0 {
|
|
1490
|
+
return index
|
|
1491
|
+
}
|
|
1492
|
+
index += next
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
func sdkConsumeRuntimeImportPrefix(text string) int {
|
|
1497
|
+
for _, prefix := range []string{
|
|
1498
|
+
"\"use strict\";\n",
|
|
1499
|
+
"'use strict';\n",
|
|
1500
|
+
"/* @ttsc-rewritten */\n",
|
|
1501
|
+
} {
|
|
1502
|
+
if strings.HasPrefix(text, prefix) {
|
|
1503
|
+
return len(prefix)
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
return 0
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
func sourceLineIndent(source string, pos int) string {
|
|
1510
|
+
if pos < 0 || pos > len(source) {
|
|
1511
|
+
return ""
|
|
1512
|
+
}
|
|
1513
|
+
start := strings.LastIndex(source[:pos], "\n")
|
|
1514
|
+
if start < 0 {
|
|
1515
|
+
start = 0
|
|
1516
|
+
} else {
|
|
1517
|
+
start++
|
|
1518
|
+
}
|
|
1519
|
+
end := start
|
|
1520
|
+
for end < len(source) && (source[end] == ' ' || source[end] == '\t') {
|
|
1521
|
+
end++
|
|
1522
|
+
}
|
|
1523
|
+
return source[start:end]
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
func nestiaSDKDiagnostic(site nestiaSDKSite, message string) typiaTransformDiagnostic {
|
|
1527
|
+
line, column := 0, 0
|
|
1528
|
+
if site.File != nil && site.Method != nil {
|
|
1529
|
+
if pos := site.Method.Pos(); pos >= 0 {
|
|
1530
|
+
l, c := shimscanner.GetECMALineAndByteOffsetOfPosition(site.File, pos)
|
|
1531
|
+
line, column = l+1, c+1
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
return typiaTransformDiagnostic{
|
|
1535
|
+
File: site.FilePath,
|
|
1536
|
+
Line: line,
|
|
1537
|
+
Column: column,
|
|
1538
|
+
Code: "nestia.sdk.OperationMetadata",
|
|
1539
|
+
Message: message,
|
|
1540
|
+
}
|
|
1541
|
+
}
|