@zyno-io/ts-reflection 26.803.2224
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/README.md +13 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +966 -0
- package/dist/reflection/annotations.d.ts +15 -0
- package/dist/reflection/annotations.d.ts.map +1 -0
- package/dist/reflection/compact-metadata.d.ts +18 -0
- package/dist/reflection/compact-metadata.d.ts.map +1 -0
- package/dist/reflection/conversion.d.ts +15 -0
- package/dist/reflection/conversion.d.ts.map +1 -0
- package/dist/reflection/deserializer.d.ts +15 -0
- package/dist/reflection/deserializer.d.ts.map +1 -0
- package/dist/reflection/errors.d.ts +8 -0
- package/dist/reflection/errors.d.ts.map +1 -0
- package/dist/reflection/index.d.ts +10 -0
- package/dist/reflection/index.d.ts.map +1 -0
- package/dist/reflection/metadata-store.d.ts +20 -0
- package/dist/reflection/metadata-store.d.ts.map +1 -0
- package/dist/reflection/model.d.ts +273 -0
- package/dist/reflection/model.d.ts.map +1 -0
- package/dist/reflection/primitive-conversion.d.ts +2 -0
- package/dist/reflection/primitive-conversion.d.ts.map +1 -0
- package/dist/reflection/reflection-class.d.ts +60 -0
- package/dist/reflection/reflection-class.d.ts.map +1 -0
- package/dist/reflection/type-utils.d.ts +34 -0
- package/dist/reflection/type-utils.d.ts.map +1 -0
- package/dist/type-compiler/download-prebuilt.cjs +114 -0
- package/dist/type-compiler/go/ast_expression.go +187 -0
- package/dist/type-compiler/go/ast_metadata.go +388 -0
- package/dist/type-compiler/go/collect.go +963 -0
- package/dist/type-compiler/go/compact_metadata.go +553 -0
- package/dist/type-compiler/go/emission_plan.go +340 -0
- package/dist/type-compiler/go/emit_ast.go +557 -0
- package/dist/type-compiler/go/emit_ast_test.go +558 -0
- package/dist/type-compiler/go/go.mod +10 -0
- package/dist/type-compiler/go/plugin.go +359 -0
- package/dist/type-compiler/go/plugin_test.go +1206 -0
- package/dist/type-compiler/go/precompute.go +86 -0
- package/dist/type-compiler/go/receive_type.go +912 -0
- package/dist/type-compiler/go/resolve.go +265 -0
- package/dist/type-compiler/go/source_scan.go +51 -0
- package/dist/type-compiler/go/text_parse.go +734 -0
- package/dist/type-compiler/go/type_expr.go +1291 -0
- package/dist/type-compiler/go/typia_expr.go +2316 -0
- package/dist/type-compiler/index.cjs +43 -0
- package/dist/type-compiler/pnp.cjs +474 -0
- package/dist/type-compiler/prebuilt.cjs +324 -0
- package/dist/type-metadata-runtime.cjs +1 -0
- package/dist/type-metadata-runtime.d.ts +2 -0
- package/dist/type-metadata-runtime.d.ts.map +1 -0
- package/dist/type-metadata-runtime.js +107 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/primitives.d.ts +33 -0
- package/dist/types/primitives.d.ts.map +1 -0
- package/dist/types/runtime.d.ts +2 -0
- package/dist/types/runtime.d.ts.map +1 -0
- package/dist/types/type-annotations.d.ts +28 -0
- package/dist/types/type-annotations.d.ts.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,963 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"regexp"
|
|
8
|
+
"sort"
|
|
9
|
+
"strconv"
|
|
10
|
+
"strings"
|
|
11
|
+
|
|
12
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
13
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
14
|
+
|
|
15
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
func collectRegistry(prog *driver.Program, cwd string, emitTypeAliases bool, emitUndecoratedMethods bool) *registry {
|
|
19
|
+
reg := ®istry{
|
|
20
|
+
files: map[string]*fileInfo{},
|
|
21
|
+
byPath: map[string]*fileInfo{},
|
|
22
|
+
checker: prog.Checker,
|
|
23
|
+
typiaCache: map[typiaCacheKey]string{},
|
|
24
|
+
typiaFailures: map[*shimchecker.Type]bool{},
|
|
25
|
+
classes: map[string]*classInfo{},
|
|
26
|
+
external: map[string]map[string][]functionInfo{},
|
|
27
|
+
externalPackageRoots: loadPnpExternalPackageRoots(cwd),
|
|
28
|
+
}
|
|
29
|
+
for _, file := range prog.TSProgram.SourceFiles() {
|
|
30
|
+
if shouldSkipFile(file.FileName(), cwd) {
|
|
31
|
+
continue
|
|
32
|
+
}
|
|
33
|
+
info := &fileInfo{
|
|
34
|
+
file: file,
|
|
35
|
+
moduleKey: moduleKey(file.FileName()),
|
|
36
|
+
precompute: shouldPrecomputeFile(file.FileName()),
|
|
37
|
+
decoratedMethodsOnly: !emitUndecoratedMethods,
|
|
38
|
+
aliases: map[string]aliasInfo{},
|
|
39
|
+
interfaces: map[string][]interfaceInfo{},
|
|
40
|
+
enums: map[string]enumInfo{},
|
|
41
|
+
classes: []*classInfo{},
|
|
42
|
+
functions: map[string][]functionInfo{},
|
|
43
|
+
imports: map[string]importRef{},
|
|
44
|
+
reexports: map[string]importRef{},
|
|
45
|
+
}
|
|
46
|
+
reg.files[file.FileName()] = info
|
|
47
|
+
reg.byPath[info.moduleKey] = info
|
|
48
|
+
collectTextDeclarations(info)
|
|
49
|
+
}
|
|
50
|
+
for _, info := range reg.files {
|
|
51
|
+
collectImports(info, reg)
|
|
52
|
+
collectReexports(info, reg)
|
|
53
|
+
}
|
|
54
|
+
for _, info := range reg.files {
|
|
55
|
+
collectAstDeclarations(info, reg)
|
|
56
|
+
}
|
|
57
|
+
for _, info := range reg.files {
|
|
58
|
+
collectGenericCalls(info, reg)
|
|
59
|
+
}
|
|
60
|
+
precomputeMetadataExpressions(reg, emitTypeAliases)
|
|
61
|
+
return reg
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Yarn PnP keeps JavaScript packages inside ZipFS, which Node can read but the
|
|
65
|
+
// native compiler cannot. The descriptor copies just imported declaration files
|
|
66
|
+
// to this map before starting the host. Other package managers leave the map
|
|
67
|
+
// empty and continue through the normal node_modules lookup.
|
|
68
|
+
func loadPnpExternalPackageRoots(cwd string) map[string]string {
|
|
69
|
+
if cwd == "" {
|
|
70
|
+
return map[string]string{}
|
|
71
|
+
}
|
|
72
|
+
contents, err := os.ReadFile(filepath.Join(cwd, ".yarn", "tsf-pnp", "external-package-roots.json"))
|
|
73
|
+
if err != nil {
|
|
74
|
+
return map[string]string{}
|
|
75
|
+
}
|
|
76
|
+
roots := map[string]string{}
|
|
77
|
+
if err := json.Unmarshal(contents, &roots); err != nil {
|
|
78
|
+
return map[string]string{}
|
|
79
|
+
}
|
|
80
|
+
for packageName, root := range roots {
|
|
81
|
+
if stat, err := os.Stat(root); err != nil || !stat.IsDir() {
|
|
82
|
+
delete(roots, packageName)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return roots
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
func shouldSkipFile(fileName string, cwd string) bool {
|
|
89
|
+
slash := filepath.ToSlash(fileName)
|
|
90
|
+
return isTypeScriptLibDeclaration(slash) ||
|
|
91
|
+
isAmbientTypesPackageDeclaration(slash) ||
|
|
92
|
+
isProjectBuildOutputFile(fileName, cwd) ||
|
|
93
|
+
strings.HasSuffix(slash, "/src/reflection.ts")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
func shouldPrecomputeFile(fileName string) bool {
|
|
97
|
+
slash := filepath.ToSlash(fileName)
|
|
98
|
+
return !strings.HasSuffix(slash, ".d.ts") && !strings.Contains(slash, "/node_modules/")
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
func isTypeScriptLibDeclaration(slash string) bool {
|
|
102
|
+
base := filepath.Base(slash)
|
|
103
|
+
return strings.HasPrefix(base, "lib.") && strings.HasSuffix(base, ".d.ts")
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func isAmbientTypesPackageDeclaration(slash string) bool {
|
|
107
|
+
return strings.Contains(slash, "/node_modules/@types/") && strings.HasSuffix(slash, ".d.ts")
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
func isProjectBuildOutputFile(fileName string, cwd string) bool {
|
|
111
|
+
if cwd == "" {
|
|
112
|
+
return false
|
|
113
|
+
}
|
|
114
|
+
rel, err := filepath.Rel(cwd, fileName)
|
|
115
|
+
if err != nil || strings.HasPrefix(rel, "..") || filepath.IsAbs(rel) {
|
|
116
|
+
return false
|
|
117
|
+
}
|
|
118
|
+
rel = filepath.ToSlash(rel)
|
|
119
|
+
first, _, _ := strings.Cut(rel, "/")
|
|
120
|
+
return first == "dist" || strings.HasPrefix(first, "dist-")
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
func moduleKey(fileName string) string {
|
|
124
|
+
slash := filepath.ToSlash(fileName)
|
|
125
|
+
lower := strings.ToLower(slash)
|
|
126
|
+
for _, suffix := range []string{".d.mts", ".d.cts", ".d.ts"} {
|
|
127
|
+
if strings.HasSuffix(lower, suffix) {
|
|
128
|
+
return filepath.Clean(slash[:len(slash)-len(suffix)])
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
ext := strings.ToLower(filepath.Ext(slash))
|
|
132
|
+
if ext == ".mts" || ext == ".cts" {
|
|
133
|
+
return filepath.Clean(slash)
|
|
134
|
+
}
|
|
135
|
+
return filepath.Clean(strings.TrimSuffix(slash, filepath.Ext(slash)))
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
func moduleSpecifier(fromFile string, toFile string) string {
|
|
139
|
+
return moduleSpecifierForOutput(fromFile, toFile, false)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
func moduleSpecifierForOutput(fromFile string, toFile string, esm bool) string {
|
|
143
|
+
fromDir := filepath.Dir(fromFile)
|
|
144
|
+
target := strings.TrimSuffix(toFile, filepath.Ext(toFile)) + outputImportExtension(toFile, esm)
|
|
145
|
+
spec, err := filepath.Rel(fromDir, target)
|
|
146
|
+
if err != nil {
|
|
147
|
+
spec = target
|
|
148
|
+
}
|
|
149
|
+
spec = filepath.ToSlash(spec)
|
|
150
|
+
if !strings.HasPrefix(spec, ".") {
|
|
151
|
+
spec = "./" + spec
|
|
152
|
+
}
|
|
153
|
+
return spec
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
func outputImportExtension(sourceFile string, esm bool) string {
|
|
157
|
+
switch strings.ToLower(filepath.Ext(sourceFile)) {
|
|
158
|
+
case ".mts":
|
|
159
|
+
return ".mjs"
|
|
160
|
+
case ".cts":
|
|
161
|
+
return ".cjs"
|
|
162
|
+
case ".ts", ".tsx":
|
|
163
|
+
if esm {
|
|
164
|
+
return ".js"
|
|
165
|
+
}
|
|
166
|
+
return ""
|
|
167
|
+
default:
|
|
168
|
+
return ""
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
func collectTextDeclarations(info *fileInfo) {
|
|
173
|
+
text := info.file.Text()
|
|
174
|
+
re := regexp.MustCompile(`(?m)^\s*(export\s+)?(?:declare\s+)?interface\s+([A-Za-z_$][\w$]*)\b`)
|
|
175
|
+
search := 0
|
|
176
|
+
for {
|
|
177
|
+
loc := re.FindStringSubmatchIndex(text[search:])
|
|
178
|
+
if loc == nil {
|
|
179
|
+
break
|
|
180
|
+
}
|
|
181
|
+
start := search + loc[0]
|
|
182
|
+
name := text[search+loc[4] : search+loc[5]]
|
|
183
|
+
exported := loc[2] >= 0
|
|
184
|
+
afterName := search + loc[1]
|
|
185
|
+
openRel := strings.IndexByte(text[afterName:], '{')
|
|
186
|
+
if openRel < 0 {
|
|
187
|
+
break
|
|
188
|
+
}
|
|
189
|
+
open := afterName + openRel
|
|
190
|
+
close := findBalanced(text, open, '{', '}')
|
|
191
|
+
if close < 0 {
|
|
192
|
+
search = open + 1
|
|
193
|
+
continue
|
|
194
|
+
}
|
|
195
|
+
body := strings.TrimSpace(text[open+1 : close])
|
|
196
|
+
header := text[afterName:open]
|
|
197
|
+
params, defaults := interfaceTypeParametersFromHeader(header)
|
|
198
|
+
info.interfaces[name] = append(info.interfaces[name], interfaceInfo{
|
|
199
|
+
body: body, params: params, defaults: defaults, extends: interfaceExtendsFromHeader(header), exported: exported, pos: start, source: "text",
|
|
200
|
+
})
|
|
201
|
+
search = close + 1
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
enumRe := regexp.MustCompile(`(?m)^\s*(?:export\s+)?(?:declare\s+)?enum\s+([A-Za-z_$][\w$]*)\b`)
|
|
205
|
+
search = 0
|
|
206
|
+
for {
|
|
207
|
+
loc := enumRe.FindStringSubmatchIndex(text[search:])
|
|
208
|
+
if loc == nil {
|
|
209
|
+
break
|
|
210
|
+
}
|
|
211
|
+
start := search + loc[0]
|
|
212
|
+
name := text[search+loc[2] : search+loc[3]]
|
|
213
|
+
afterName := search + loc[1]
|
|
214
|
+
openRel := strings.IndexByte(text[afterName:], '{')
|
|
215
|
+
if openRel < 0 {
|
|
216
|
+
break
|
|
217
|
+
}
|
|
218
|
+
open := afterName + openRel
|
|
219
|
+
close := findBalanced(text, open, '{', '}')
|
|
220
|
+
if close < 0 {
|
|
221
|
+
search = open + 1
|
|
222
|
+
continue
|
|
223
|
+
}
|
|
224
|
+
body := strings.TrimSpace(text[open+1 : close])
|
|
225
|
+
info.enums[name] = enumInfo{name: name, values: enumValuesFromBody(body), pos: start}
|
|
226
|
+
search = close + 1
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
func collectAstDeclarations(info *fileInfo, reg *registry) {
|
|
231
|
+
var walk func(*shimast.Node)
|
|
232
|
+
walk = func(node *shimast.Node) {
|
|
233
|
+
if node == nil {
|
|
234
|
+
return
|
|
235
|
+
}
|
|
236
|
+
if node.Kind == shimast.KindClassDeclaration {
|
|
237
|
+
if class := classFromNode(info, node); class != nil {
|
|
238
|
+
info.classes = append(info.classes, class)
|
|
239
|
+
reg.classes[class.name] = class
|
|
240
|
+
}
|
|
241
|
+
} else if node.Kind == shimast.KindFunctionDeclaration {
|
|
242
|
+
if fn := functionFromNode(info.file, node); fn.name != "" {
|
|
243
|
+
info.functions[fn.name] = append(info.functions[fn.name], fn)
|
|
244
|
+
}
|
|
245
|
+
} else if node.Kind == shimast.KindTypeAliasDeclaration {
|
|
246
|
+
if alias := aliasFromNode(info.file, node); alias.body != "" && node.Name() != nil {
|
|
247
|
+
info.aliases[node.Name().Text()] = alias
|
|
248
|
+
}
|
|
249
|
+
} else if node.Kind == shimast.KindInterfaceDeclaration {
|
|
250
|
+
if node.Name() != nil {
|
|
251
|
+
name := node.Name().Text()
|
|
252
|
+
decl := interfaceFromNode(info.file, node)
|
|
253
|
+
if index := textInterfaceDeclarationNearIndex(info.interfaces[name], node.Pos()); index >= 0 {
|
|
254
|
+
info.interfaces[name][index] = decl
|
|
255
|
+
} else {
|
|
256
|
+
info.interfaces[name] = append(info.interfaces[name], decl)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
261
|
+
walk(child)
|
|
262
|
+
return false
|
|
263
|
+
})
|
|
264
|
+
}
|
|
265
|
+
walk(info.file.AsNode())
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
func hasTextInterfaceDeclarationNear(decls []interfaceInfo, pos int) bool {
|
|
269
|
+
return textInterfaceDeclarationNearIndex(decls, pos) >= 0
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
func textInterfaceDeclarationNearIndex(decls []interfaceInfo, pos int) int {
|
|
273
|
+
for i, decl := range decls {
|
|
274
|
+
if decl.source == "text" && intAbs(decl.pos-pos) <= 64 {
|
|
275
|
+
return i
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return -1
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
func intAbs(value int) int {
|
|
282
|
+
if value < 0 {
|
|
283
|
+
return -value
|
|
284
|
+
}
|
|
285
|
+
return value
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
func aliasFromNode(file *shimast.SourceFile, node *shimast.Node) aliasInfo {
|
|
289
|
+
params := []string{}
|
|
290
|
+
defaults := []string{}
|
|
291
|
+
for _, param := range node.TypeParameters() {
|
|
292
|
+
if param.Name() != nil {
|
|
293
|
+
params = append(params, param.Name().Text())
|
|
294
|
+
defaultText := ""
|
|
295
|
+
if defaultType := param.AsTypeParameterDeclaration().DefaultType; defaultType != nil {
|
|
296
|
+
defaultText = nodeText(file, defaultType)
|
|
297
|
+
}
|
|
298
|
+
defaults = append(defaults, defaultText)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return aliasInfo{
|
|
302
|
+
body: nodeText(file, node.Type()),
|
|
303
|
+
params: params,
|
|
304
|
+
defaults: defaults,
|
|
305
|
+
typeNode: node.Type(),
|
|
306
|
+
exported: node.ModifierFlags()&shimast.ModifierFlagsExport != 0,
|
|
307
|
+
pos: node.Pos(),
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
func interfaceFromNode(file *shimast.SourceFile, node *shimast.Node) interfaceInfo {
|
|
312
|
+
params := []string{}
|
|
313
|
+
defaults := []string{}
|
|
314
|
+
for _, param := range node.TypeParameters() {
|
|
315
|
+
if param.Name() == nil {
|
|
316
|
+
continue
|
|
317
|
+
}
|
|
318
|
+
params = append(params, param.Name().Text())
|
|
319
|
+
defaultText := ""
|
|
320
|
+
if defaultType := param.AsTypeParameterDeclaration().DefaultType; defaultType != nil {
|
|
321
|
+
defaultText = nodeText(file, defaultType)
|
|
322
|
+
}
|
|
323
|
+
defaults = append(defaults, defaultText)
|
|
324
|
+
}
|
|
325
|
+
return interfaceInfo{
|
|
326
|
+
body: interfaceBodyFromNode(file, node),
|
|
327
|
+
params: params,
|
|
328
|
+
defaults: defaults,
|
|
329
|
+
extends: interfaceExtendsFromNode(file, node),
|
|
330
|
+
properties: interfacePropertiesFromNode(file, node),
|
|
331
|
+
exported: node.ModifierFlags()&shimast.ModifierFlagsExport != 0,
|
|
332
|
+
pos: node.Pos(),
|
|
333
|
+
source: "ast",
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
func interfacePropertiesFromNode(file *shimast.SourceFile, node *shimast.Node) []utilityProperty {
|
|
338
|
+
props := []utilityProperty{}
|
|
339
|
+
for _, member := range node.Members() {
|
|
340
|
+
if member.Kind != shimast.KindPropertySignature {
|
|
341
|
+
continue
|
|
342
|
+
}
|
|
343
|
+
name := memberName(file, member)
|
|
344
|
+
if name == "" || member.Type() == nil {
|
|
345
|
+
continue
|
|
346
|
+
}
|
|
347
|
+
props = append(props, utilityProperty{
|
|
348
|
+
name: name,
|
|
349
|
+
typeText: nodeText(file, member.Type()),
|
|
350
|
+
typeNode: member.Type(),
|
|
351
|
+
optional: member.QuestionToken() != nil,
|
|
352
|
+
})
|
|
353
|
+
}
|
|
354
|
+
return props
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
func interfaceBodyFromNode(file *shimast.SourceFile, node *shimast.Node) string {
|
|
358
|
+
if name := node.Name(); name != nil {
|
|
359
|
+
text := file.Text()
|
|
360
|
+
start := name.End()
|
|
361
|
+
end := node.End()
|
|
362
|
+
if start >= 0 && start < len(text) && end > start && end <= len(text) {
|
|
363
|
+
openRel := strings.IndexByte(text[start:end], '{')
|
|
364
|
+
if openRel >= 0 {
|
|
365
|
+
open := start + openRel
|
|
366
|
+
close := findBalanced(text, open, '{', '}')
|
|
367
|
+
if close >= 0 && close <= end {
|
|
368
|
+
return strings.TrimSpace(text[open+1 : close])
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
members := []string{}
|
|
375
|
+
for _, member := range node.Members() {
|
|
376
|
+
members = append(members, nodeText(file, member))
|
|
377
|
+
}
|
|
378
|
+
return strings.Join(members, ";")
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
func interfaceExtendsFromNode(file *shimast.SourceFile, node *shimast.Node) []string {
|
|
382
|
+
name := node.Name()
|
|
383
|
+
if name == nil {
|
|
384
|
+
return nil
|
|
385
|
+
}
|
|
386
|
+
text := file.Text()
|
|
387
|
+
start := name.End()
|
|
388
|
+
end := node.End()
|
|
389
|
+
if start < 0 || start >= len(text) || end <= start || end > len(text) {
|
|
390
|
+
return nil
|
|
391
|
+
}
|
|
392
|
+
openRel := strings.IndexByte(text[start:end], '{')
|
|
393
|
+
if openRel < 0 {
|
|
394
|
+
return nil
|
|
395
|
+
}
|
|
396
|
+
return interfaceExtendsFromHeader(text[start : start+openRel])
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
func interfaceExtendsFromHeader(header string) []string {
|
|
400
|
+
header = strings.TrimSpace(stripTypeComments(header))
|
|
401
|
+
if strings.HasPrefix(header, "<") {
|
|
402
|
+
if end := findBalanced(header, 0, '<', '>'); end >= 0 {
|
|
403
|
+
header = strings.TrimSpace(header[end+1:])
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
idx := topLevelKeywordIndex(header, "extends")
|
|
407
|
+
if idx < 0 {
|
|
408
|
+
return nil
|
|
409
|
+
}
|
|
410
|
+
refs := []string{}
|
|
411
|
+
for _, part := range splitTop(header[idx+len("extends"):], ",") {
|
|
412
|
+
part = strings.TrimSpace(part)
|
|
413
|
+
if part != "" {
|
|
414
|
+
refs = append(refs, part)
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return refs
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
func interfaceTypeParametersFromHeader(header string) ([]string, []string) {
|
|
421
|
+
header = strings.TrimSpace(stripTypeComments(header))
|
|
422
|
+
if !strings.HasPrefix(header, "<") {
|
|
423
|
+
return nil, nil
|
|
424
|
+
}
|
|
425
|
+
end := findBalanced(header, 0, '<', '>')
|
|
426
|
+
if end < 0 {
|
|
427
|
+
return nil, nil
|
|
428
|
+
}
|
|
429
|
+
params := []string{}
|
|
430
|
+
defaults := []string{}
|
|
431
|
+
for _, part := range splitTop(header[1:end], ",") {
|
|
432
|
+
part = strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(part), "const "))
|
|
433
|
+
if part == "" {
|
|
434
|
+
continue
|
|
435
|
+
}
|
|
436
|
+
defaultText := ""
|
|
437
|
+
if index := topLevelEquals(part); index >= 0 {
|
|
438
|
+
defaultText = strings.TrimSpace(part[index+1:])
|
|
439
|
+
part = strings.TrimSpace(part[:index])
|
|
440
|
+
}
|
|
441
|
+
if index := topLevelKeywordIndex(part, "extends"); index >= 0 {
|
|
442
|
+
part = strings.TrimSpace(part[:index])
|
|
443
|
+
}
|
|
444
|
+
if !isIdentifierName(part) {
|
|
445
|
+
continue
|
|
446
|
+
}
|
|
447
|
+
params = append(params, part)
|
|
448
|
+
defaults = append(defaults, defaultText)
|
|
449
|
+
}
|
|
450
|
+
return params, defaults
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
func topLevelKeywordIndex(input string, keyword string) int {
|
|
454
|
+
depthAngle, depthBrace, depthParen, depthBracket, quote := 0, 0, 0, 0, rune(0)
|
|
455
|
+
for i, r := range input {
|
|
456
|
+
if quote != 0 {
|
|
457
|
+
if r == quote {
|
|
458
|
+
quote = 0
|
|
459
|
+
}
|
|
460
|
+
continue
|
|
461
|
+
}
|
|
462
|
+
switch r {
|
|
463
|
+
case '\'', '"', '`':
|
|
464
|
+
quote = r
|
|
465
|
+
case '<':
|
|
466
|
+
depthAngle++
|
|
467
|
+
case '>':
|
|
468
|
+
if depthAngle > 0 {
|
|
469
|
+
depthAngle--
|
|
470
|
+
}
|
|
471
|
+
case '{':
|
|
472
|
+
depthBrace++
|
|
473
|
+
case '}':
|
|
474
|
+
if depthBrace > 0 {
|
|
475
|
+
depthBrace--
|
|
476
|
+
}
|
|
477
|
+
case '(':
|
|
478
|
+
depthParen++
|
|
479
|
+
case ')':
|
|
480
|
+
if depthParen > 0 {
|
|
481
|
+
depthParen--
|
|
482
|
+
}
|
|
483
|
+
case '[':
|
|
484
|
+
depthBracket++
|
|
485
|
+
case ']':
|
|
486
|
+
if depthBracket > 0 {
|
|
487
|
+
depthBracket--
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
if depthAngle == 0 && depthBrace == 0 && depthParen == 0 && depthBracket == 0 && strings.HasPrefix(input[i:], keyword) {
|
|
491
|
+
beforeOK := i == 0 || !isIdent(input[i-1])
|
|
492
|
+
after := i + len(keyword)
|
|
493
|
+
afterOK := after >= len(input) || !isIdent(input[after])
|
|
494
|
+
if beforeOK && afterOK {
|
|
495
|
+
return i
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return -1
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
func topLevelColon(input string) int {
|
|
503
|
+
return topLevelByteIndex(input, ':')
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
func topLevelEquals(input string) int {
|
|
507
|
+
return topLevelByteIndex(input, '=')
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
func topLevelByteIndex(input string, target byte) int {
|
|
511
|
+
depthAngle, depthBrace, depthParen, depthBracket, quote := 0, 0, 0, 0, rune(0)
|
|
512
|
+
for i, r := range input {
|
|
513
|
+
if quote != 0 {
|
|
514
|
+
if r == quote {
|
|
515
|
+
quote = 0
|
|
516
|
+
}
|
|
517
|
+
continue
|
|
518
|
+
}
|
|
519
|
+
switch r {
|
|
520
|
+
case '\'', '"', '`':
|
|
521
|
+
quote = r
|
|
522
|
+
case '<':
|
|
523
|
+
depthAngle++
|
|
524
|
+
case '>':
|
|
525
|
+
if depthAngle > 0 {
|
|
526
|
+
depthAngle--
|
|
527
|
+
}
|
|
528
|
+
case '{':
|
|
529
|
+
depthBrace++
|
|
530
|
+
case '}':
|
|
531
|
+
if depthBrace > 0 {
|
|
532
|
+
depthBrace--
|
|
533
|
+
}
|
|
534
|
+
case '(':
|
|
535
|
+
depthParen++
|
|
536
|
+
case ')':
|
|
537
|
+
if depthParen > 0 {
|
|
538
|
+
depthParen--
|
|
539
|
+
}
|
|
540
|
+
case '[':
|
|
541
|
+
depthBracket++
|
|
542
|
+
case ']':
|
|
543
|
+
if depthBracket > 0 {
|
|
544
|
+
depthBracket--
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if depthAngle == 0 && depthBrace == 0 && depthParen == 0 && depthBracket == 0 && input[i] == target {
|
|
548
|
+
return i
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return -1
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
func interfaceFullBody(info *fileInfo, reg *registry, decl interfaceInfo, seen map[string]bool) string {
|
|
555
|
+
if seen == nil {
|
|
556
|
+
seen = map[string]bool{}
|
|
557
|
+
}
|
|
558
|
+
key := info.moduleKey + "\x00" + strconv.Itoa(decl.pos)
|
|
559
|
+
if seen[key] {
|
|
560
|
+
return decl.body
|
|
561
|
+
}
|
|
562
|
+
seen[key] = true
|
|
563
|
+
parts := []string{}
|
|
564
|
+
for _, base := range decl.extends {
|
|
565
|
+
baseDecl, owner, _, ok := resolveInterfaceDeclRefAt(info, reg, base, decl.pos)
|
|
566
|
+
if !ok {
|
|
567
|
+
continue
|
|
568
|
+
}
|
|
569
|
+
baseDecl = instantiateInterfaceReference(baseDecl, base)
|
|
570
|
+
if body := strings.TrimSpace(interfaceFullBody(owner, reg, baseDecl, seen)); body != "" {
|
|
571
|
+
parts = append(parts, body)
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
if body := strings.TrimSpace(decl.body); body != "" {
|
|
575
|
+
parts = append(parts, body)
|
|
576
|
+
}
|
|
577
|
+
return strings.Join(parts, ";\n")
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
func interfaceFullProperties(info *fileInfo, reg *registry, decl interfaceInfo, seen map[string]bool) []utilityProperty {
|
|
581
|
+
if seen == nil {
|
|
582
|
+
seen = map[string]bool{}
|
|
583
|
+
}
|
|
584
|
+
key := info.moduleKey + "\x00" + strconv.Itoa(decl.pos)
|
|
585
|
+
if seen[key] {
|
|
586
|
+
return interfaceOwnProperties(info, decl)
|
|
587
|
+
}
|
|
588
|
+
seen[key] = true
|
|
589
|
+
props := []utilityProperty{}
|
|
590
|
+
for _, base := range decl.extends {
|
|
591
|
+
baseDecl, owner, _, ok := resolveInterfaceDeclRefAt(info, reg, base, decl.pos)
|
|
592
|
+
if ok {
|
|
593
|
+
baseDecl = instantiateInterfaceReference(baseDecl, base)
|
|
594
|
+
props = append(props, interfaceFullProperties(owner, reg, baseDecl, seen)...)
|
|
595
|
+
continue
|
|
596
|
+
}
|
|
597
|
+
if baseProps, _, ok := utilitySourceProperties(info, reg, base, &typeContext{seen: map[string]bool{}, pos: decl.pos}); ok {
|
|
598
|
+
props = append(props, baseProps...)
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
props = append(props, interfaceOwnProperties(info, decl)...)
|
|
602
|
+
return props
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
func interfaceOwnProperties(info *fileInfo, decl interfaceInfo) []utilityProperty {
|
|
606
|
+
if len(decl.properties) != 0 {
|
|
607
|
+
return withUtilityPropertyOwner(decl.properties, info)
|
|
608
|
+
}
|
|
609
|
+
return withUtilityPropertyOwner(propertiesFromBody(decl.body), info)
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
func instantiateInterfaceReference(decl interfaceInfo, reference string) interfaceInfo {
|
|
613
|
+
_, args, _ := generic(strings.TrimSpace(reference))
|
|
614
|
+
return instantiateInterfaceDecl(decl, args)
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
func instantiateInterfaceDecl(decl interfaceInfo, args []string) interfaceInfo {
|
|
618
|
+
if len(decl.params) == 0 {
|
|
619
|
+
return decl
|
|
620
|
+
}
|
|
621
|
+
replacements := map[string]string{}
|
|
622
|
+
parameterPatterns := []string{}
|
|
623
|
+
for index, param := range decl.params {
|
|
624
|
+
value := "unknown"
|
|
625
|
+
if index < len(args) && strings.TrimSpace(args[index]) != "" {
|
|
626
|
+
value = strings.TrimSpace(args[index])
|
|
627
|
+
} else if index < len(decl.defaults) && strings.TrimSpace(decl.defaults[index]) != "" {
|
|
628
|
+
value = strings.TrimSpace(decl.defaults[index])
|
|
629
|
+
}
|
|
630
|
+
replacements[param] = value
|
|
631
|
+
parameterPatterns = append(parameterPatterns, regexp.QuoteMeta(param))
|
|
632
|
+
}
|
|
633
|
+
parameterPattern := regexp.MustCompile(`\b(` + strings.Join(parameterPatterns, "|") + `)\b`)
|
|
634
|
+
replace := func(value string) string {
|
|
635
|
+
return parameterPattern.ReplaceAllStringFunc(value, func(param string) string { return replacements[param] })
|
|
636
|
+
}
|
|
637
|
+
decl.body = replace(decl.body)
|
|
638
|
+
decl.extends = append([]string(nil), decl.extends...)
|
|
639
|
+
for index := range decl.extends {
|
|
640
|
+
decl.extends[index] = replace(decl.extends[index])
|
|
641
|
+
}
|
|
642
|
+
decl.properties = append([]utilityProperty(nil), decl.properties...)
|
|
643
|
+
for index := range decl.properties {
|
|
644
|
+
decl.properties[index].typeText = replace(decl.properties[index].typeText)
|
|
645
|
+
// The AST node still contains the uninstantiated type parameter.
|
|
646
|
+
decl.properties[index].typeNode = nil
|
|
647
|
+
}
|
|
648
|
+
decl.params = nil
|
|
649
|
+
decl.defaults = nil
|
|
650
|
+
return decl
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
func interfaceImplementsExpr(info *fileInfo, reg *registry, decl interfaceInfo, ctx *typeContext) string {
|
|
654
|
+
items := []string{}
|
|
655
|
+
for _, base := range decl.extends {
|
|
656
|
+
if _, _, _, ok := resolveInterfaceDeclRefAt(info, reg, base, decl.pos); ok {
|
|
657
|
+
continue
|
|
658
|
+
}
|
|
659
|
+
items = append(items, typeExprCtx(info, reg, base, ctx))
|
|
660
|
+
}
|
|
661
|
+
return strings.Join(items, ", ")
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
func interfaceRefName(raw string) string {
|
|
665
|
+
raw = strings.TrimSpace(trimParens(raw))
|
|
666
|
+
if name, _, ok := generic(raw); ok {
|
|
667
|
+
raw = name
|
|
668
|
+
}
|
|
669
|
+
return strings.TrimSpace(raw)
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
func collectImports(info *fileInfo, reg *registry) {
|
|
673
|
+
text := info.file.Text()
|
|
674
|
+
re := regexp.MustCompile(`(?m)import\s+(?:type\s+)?\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]`)
|
|
675
|
+
for _, match := range re.FindAllStringSubmatch(text, -1) {
|
|
676
|
+
target := resolveImport(info.file.FileName(), match[2], reg)
|
|
677
|
+
source := ""
|
|
678
|
+
if target != nil {
|
|
679
|
+
source = target.moduleKey
|
|
680
|
+
}
|
|
681
|
+
for _, item := range strings.Split(match[1], ",") {
|
|
682
|
+
item = strings.TrimSpace(item)
|
|
683
|
+
if item == "" {
|
|
684
|
+
continue
|
|
685
|
+
}
|
|
686
|
+
item = strings.TrimSpace(strings.TrimPrefix(item, "type "))
|
|
687
|
+
parts := regexp.MustCompile(`\s+as\s+`).Split(item, 2)
|
|
688
|
+
exportName := strings.TrimSpace(parts[0])
|
|
689
|
+
localName := exportName
|
|
690
|
+
if len(parts) == 2 {
|
|
691
|
+
localName = strings.TrimSpace(parts[1])
|
|
692
|
+
}
|
|
693
|
+
info.imports[localName] = importRef{source: source, exportName: exportName, spec: match[2]}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
func collectReexports(info *fileInfo, reg *registry) {
|
|
699
|
+
text := info.file.Text()
|
|
700
|
+
starRe := regexp.MustCompile(`(?m)export\s+\*\s+from\s+['"]([^'"]+)['"]`)
|
|
701
|
+
for _, match := range starRe.FindAllStringSubmatch(text, -1) {
|
|
702
|
+
target := resolveImport(info.file.FileName(), match[1], reg)
|
|
703
|
+
if target == nil {
|
|
704
|
+
continue
|
|
705
|
+
}
|
|
706
|
+
info.exportStar = append(info.exportStar, importRef{source: target.moduleKey, exportName: "*", spec: match[1]})
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
namedRe := regexp.MustCompile(`(?m)export\s+(?:type\s+)?\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]`)
|
|
710
|
+
for _, match := range namedRe.FindAllStringSubmatch(text, -1) {
|
|
711
|
+
target := resolveImport(info.file.FileName(), match[2], reg)
|
|
712
|
+
if target == nil {
|
|
713
|
+
continue
|
|
714
|
+
}
|
|
715
|
+
for _, item := range strings.Split(match[1], ",") {
|
|
716
|
+
item = strings.TrimSpace(item)
|
|
717
|
+
if item == "" {
|
|
718
|
+
continue
|
|
719
|
+
}
|
|
720
|
+
item = strings.TrimSpace(strings.TrimPrefix(item, "type "))
|
|
721
|
+
parts := regexp.MustCompile(`\s+as\s+`).Split(item, 2)
|
|
722
|
+
exportName := strings.TrimSpace(parts[0])
|
|
723
|
+
localName := exportName
|
|
724
|
+
if len(parts) == 2 {
|
|
725
|
+
localName = strings.TrimSpace(parts[1])
|
|
726
|
+
}
|
|
727
|
+
info.reexports[localName] = importRef{source: target.moduleKey, exportName: exportName, spec: match[2]}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
localNamedRe := regexp.MustCompile(`(?m)export\s+(?:type\s+)?\{([^}]+)\}\s*(?:;|$)`)
|
|
732
|
+
for _, match := range localNamedRe.FindAllStringSubmatch(text, -1) {
|
|
733
|
+
for _, item := range strings.Split(match[1], ",") {
|
|
734
|
+
localName, exportName, ok := parseExportItem(item)
|
|
735
|
+
if !ok {
|
|
736
|
+
continue
|
|
737
|
+
}
|
|
738
|
+
if ref, ok := info.imports[localName]; ok {
|
|
739
|
+
info.reexports[exportName] = ref
|
|
740
|
+
continue
|
|
741
|
+
}
|
|
742
|
+
if exportName != localName {
|
|
743
|
+
info.reexports[exportName] = importRef{source: info.moduleKey, exportName: localName}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
func parseExportItem(item string) (string, string, bool) {
|
|
750
|
+
item = strings.TrimSpace(item)
|
|
751
|
+
if item == "" {
|
|
752
|
+
return "", "", false
|
|
753
|
+
}
|
|
754
|
+
item = strings.TrimSpace(strings.TrimPrefix(item, "type "))
|
|
755
|
+
parts := regexp.MustCompile(`\s+as\s+`).Split(item, 2)
|
|
756
|
+
localName := strings.TrimSpace(parts[0])
|
|
757
|
+
exportName := localName
|
|
758
|
+
if len(parts) == 2 {
|
|
759
|
+
exportName = strings.TrimSpace(parts[1])
|
|
760
|
+
}
|
|
761
|
+
if localName == "" || exportName == "" {
|
|
762
|
+
return "", "", false
|
|
763
|
+
}
|
|
764
|
+
return localName, exportName, true
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
func collectGenericCalls(info *fileInfo, reg *registry) {
|
|
768
|
+
calls := []callInfo{}
|
|
769
|
+
seen := map[int]bool{}
|
|
770
|
+
var walk func(*shimast.Node)
|
|
771
|
+
walk = func(node *shimast.Node) {
|
|
772
|
+
if node == nil {
|
|
773
|
+
return
|
|
774
|
+
}
|
|
775
|
+
if node.Kind == shimast.KindCallExpression {
|
|
776
|
+
call := node.AsCallExpression()
|
|
777
|
+
if received, ok := resolvedReceiveTypeCall(info, reg, node); ok {
|
|
778
|
+
calls = append(calls, received)
|
|
779
|
+
seen[node.Pos()] = true
|
|
780
|
+
}
|
|
781
|
+
typeArgs := node.TypeArguments()
|
|
782
|
+
if len(typeArgs) > 0 && !seen[node.Pos()] {
|
|
783
|
+
name, metadataArgIndex, recognized := metadataCallDetails(reg, call)
|
|
784
|
+
if recognized {
|
|
785
|
+
argumentCount := 0
|
|
786
|
+
if call.Arguments != nil {
|
|
787
|
+
argumentCount = len(call.Arguments.Nodes)
|
|
788
|
+
}
|
|
789
|
+
if argumentCount <= metadataArgIndex {
|
|
790
|
+
calls = append(calls, callInfo{
|
|
791
|
+
name: name,
|
|
792
|
+
nodePos: node.Pos(),
|
|
793
|
+
metadataArgIndex: metadataArgIndex,
|
|
794
|
+
typeText: nodeText(info.file, typeArgs[0]),
|
|
795
|
+
typeNode: typeArgs[0],
|
|
796
|
+
preferTypia: true,
|
|
797
|
+
pos: node.Pos(),
|
|
798
|
+
})
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
804
|
+
walk(child)
|
|
805
|
+
return false
|
|
806
|
+
})
|
|
807
|
+
}
|
|
808
|
+
walk(info.file.AsNode())
|
|
809
|
+
fallbackCalls := collectReceiveTypeCalls(info, reg)
|
|
810
|
+
matchCallNodePositions(info.file, fallbackCalls)
|
|
811
|
+
for _, call := range fallbackCalls {
|
|
812
|
+
if call.nodePos >= 0 && seen[call.nodePos] {
|
|
813
|
+
continue
|
|
814
|
+
}
|
|
815
|
+
calls = append(calls, call)
|
|
816
|
+
if call.nodePos >= 0 {
|
|
817
|
+
seen[call.nodePos] = true
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
sort.Slice(calls, func(i, j int) bool { return calls[i].pos < calls[j].pos })
|
|
821
|
+
matchCallNodePositions(info.file, calls)
|
|
822
|
+
info.calls = calls
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
func isMetadataCallName(name string) bool {
|
|
826
|
+
switch name {
|
|
827
|
+
case "deserialize", "validate", "validatedDeserialize", "typeOf":
|
|
828
|
+
return true
|
|
829
|
+
default:
|
|
830
|
+
return false
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
func metadataCallDetails(reg *registry, call *shimast.CallExpression) (string, int, bool) {
|
|
835
|
+
if reg == nil || reg.checker == nil || call == nil {
|
|
836
|
+
return "", 0, false
|
|
837
|
+
}
|
|
838
|
+
signature := reg.checker.GetResolvedSignature(call.AsNode())
|
|
839
|
+
if signature == nil {
|
|
840
|
+
return "", 0, false
|
|
841
|
+
}
|
|
842
|
+
declaration := signature.Declaration()
|
|
843
|
+
if declaration == nil {
|
|
844
|
+
return "", 0, false
|
|
845
|
+
}
|
|
846
|
+
source := shimast.GetSourceFileOfNode(declaration)
|
|
847
|
+
if source == nil || !isReflectionMetadataDeclarationFile(source.FileName()) {
|
|
848
|
+
return "", 0, false
|
|
849
|
+
}
|
|
850
|
+
name := ""
|
|
851
|
+
if declaration.Name() != nil {
|
|
852
|
+
name = declaration.Name().Text()
|
|
853
|
+
}
|
|
854
|
+
if name == "" {
|
|
855
|
+
if typ := reg.checker.GetTypeAtLocation(declaration); typ != nil && typ.Symbol() != nil {
|
|
856
|
+
name = typ.Symbol().Name
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
switch name {
|
|
860
|
+
case "typeOf":
|
|
861
|
+
return name, 0, true
|
|
862
|
+
case "deserialize", "validate":
|
|
863
|
+
return name, 1, true
|
|
864
|
+
case "assert", "is":
|
|
865
|
+
return name, 2, true
|
|
866
|
+
case "cast", "validatedDeserialize":
|
|
867
|
+
return name, 4, true
|
|
868
|
+
default:
|
|
869
|
+
return "", 0, false
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
func isReflectionMetadataDeclarationFile(fileName string) bool {
|
|
874
|
+
slash := filepath.ToSlash(fileName)
|
|
875
|
+
isReflectionPackage := strings.Contains(slash, "/ts-reflection/") || strings.Contains(slash, "/packages/reflection/")
|
|
876
|
+
isLegacyFoundationPackage := strings.Contains(slash, "/ts-server-foundation/")
|
|
877
|
+
if !isReflectionPackage && !isLegacyFoundationPackage {
|
|
878
|
+
return false
|
|
879
|
+
}
|
|
880
|
+
for _, suffix := range []string{
|
|
881
|
+
"/src/reflection/conversion.ts",
|
|
882
|
+
"/src/reflection/conversion.d.ts",
|
|
883
|
+
"/dist/reflection/conversion.d.ts",
|
|
884
|
+
"/dist/src/reflection/conversion.d.ts",
|
|
885
|
+
"/src/reflection/reflection-class.ts",
|
|
886
|
+
"/src/reflection/reflection-class.d.ts",
|
|
887
|
+
"/dist/reflection/reflection-class.d.ts",
|
|
888
|
+
"/dist/src/reflection/reflection-class.d.ts",
|
|
889
|
+
} {
|
|
890
|
+
if strings.HasSuffix(slash, suffix) {
|
|
891
|
+
return true
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
return false
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
func matchCallNodePositions(file *shimast.SourceFile, calls []callInfo) {
|
|
898
|
+
if file == nil || len(calls) == 0 {
|
|
899
|
+
return
|
|
900
|
+
}
|
|
901
|
+
type candidate struct {
|
|
902
|
+
pos int
|
|
903
|
+
end int
|
|
904
|
+
start int
|
|
905
|
+
}
|
|
906
|
+
candidates := []candidate{}
|
|
907
|
+
var walk func(*shimast.Node)
|
|
908
|
+
walk = func(node *shimast.Node) {
|
|
909
|
+
if node == nil {
|
|
910
|
+
return
|
|
911
|
+
}
|
|
912
|
+
if node.Kind == shimast.KindCallExpression {
|
|
913
|
+
call := node.AsCallExpression()
|
|
914
|
+
start := node.Pos()
|
|
915
|
+
end := node.End()
|
|
916
|
+
if call != nil && call.Expression != nil {
|
|
917
|
+
start = call.Expression.Pos()
|
|
918
|
+
end = call.Expression.End()
|
|
919
|
+
}
|
|
920
|
+
candidates = append(candidates, candidate{pos: node.Pos(), start: start, end: end})
|
|
921
|
+
}
|
|
922
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
923
|
+
walk(child)
|
|
924
|
+
return false
|
|
925
|
+
})
|
|
926
|
+
}
|
|
927
|
+
walk(file.AsNode())
|
|
928
|
+
for index := range calls {
|
|
929
|
+
if calls[index].nodePos >= 0 {
|
|
930
|
+
continue
|
|
931
|
+
}
|
|
932
|
+
bestSpan := 0
|
|
933
|
+
for _, item := range candidates {
|
|
934
|
+
if item.start <= calls[index].pos && calls[index].pos < item.end {
|
|
935
|
+
span := item.end - item.start
|
|
936
|
+
if bestSpan == 0 || span < bestSpan {
|
|
937
|
+
calls[index].nodePos = item.pos
|
|
938
|
+
bestSpan = span
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
func isFoundationCompatibilityImport(ref importRef) bool {
|
|
946
|
+
if ref.spec == foundationPackageSpec || ref.spec == reflectionPackageSpec {
|
|
947
|
+
return true
|
|
948
|
+
}
|
|
949
|
+
source := filepath.ToSlash(ref.source)
|
|
950
|
+
if strings.Contains(source, "/packages/reflection/src/") {
|
|
951
|
+
return strings.HasSuffix(source, "/src/index") ||
|
|
952
|
+
strings.HasSuffix(source, "/src/types/index") ||
|
|
953
|
+
strings.HasSuffix(source, "/src/reflection/index") ||
|
|
954
|
+
strings.HasSuffix(source, "/src/reflection/conversion")
|
|
955
|
+
}
|
|
956
|
+
if !strings.Contains(source, "/ts-server-foundation/src/") {
|
|
957
|
+
return false
|
|
958
|
+
}
|
|
959
|
+
return strings.HasSuffix(source, "/src/index") ||
|
|
960
|
+
strings.HasSuffix(source, "/src/types/index") ||
|
|
961
|
+
strings.HasSuffix(source, "/src/reflection/index") ||
|
|
962
|
+
strings.HasSuffix(source, "/src/reflection/conversion")
|
|
963
|
+
}
|