@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,114 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/* oxlint-disable typescript/no-require-imports -- this helper is spawned by the CommonJS plugin descriptor. */
|
|
3
|
+
|
|
4
|
+
const crypto = require('node:crypto');
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const http = require('node:http');
|
|
7
|
+
const https = require('node:https');
|
|
8
|
+
const os = require('node:os');
|
|
9
|
+
const path = require('node:path');
|
|
10
|
+
|
|
11
|
+
const MAX_BINARY_BYTES = 128 * 1024 * 1024;
|
|
12
|
+
const MAX_MANIFEST_BYTES = 64 * 1024;
|
|
13
|
+
const MAX_REDIRECTS = 5;
|
|
14
|
+
|
|
15
|
+
async function main() {
|
|
16
|
+
const input = JSON.parse(fs.readFileSync(0, 'utf8'));
|
|
17
|
+
const manifest = JSON.parse((await download(input.manifestUrl, MAX_MANIFEST_BYTES, input)).toString('utf8'));
|
|
18
|
+
validateManifest(manifest, input.expected);
|
|
19
|
+
|
|
20
|
+
const binary = await download(input.binaryUrl, MAX_BINARY_BYTES, input);
|
|
21
|
+
const digest = crypto.createHash('sha256').update(binary).digest('hex');
|
|
22
|
+
if (digest !== manifest.binarySha256) throw new Error(`binary checksum mismatch: expected ${manifest.binarySha256}, received ${digest}`);
|
|
23
|
+
if (binary.length !== manifest.binarySize) throw new Error(`binary size mismatch: expected ${manifest.binarySize}, received ${binary.length}`);
|
|
24
|
+
|
|
25
|
+
const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'tsf-type-compiler-'));
|
|
26
|
+
const temporaryBinary = path.join(temporaryDirectory, path.basename(input.destination));
|
|
27
|
+
try {
|
|
28
|
+
fs.writeFileSync(temporaryBinary, binary, { mode: 0o755 });
|
|
29
|
+
fs.mkdirSync(path.dirname(input.destination), { recursive: true });
|
|
30
|
+
if (fs.existsSync(input.destination)) return;
|
|
31
|
+
try {
|
|
32
|
+
fs.renameSync(temporaryBinary, input.destination);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if (!fs.existsSync(input.destination)) throw error;
|
|
35
|
+
}
|
|
36
|
+
if (process.platform !== 'win32') fs.chmodSync(input.destination, 0o755);
|
|
37
|
+
} finally {
|
|
38
|
+
fs.rmSync(temporaryDirectory, { force: true, recursive: true });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function validateManifest(manifest, expected) {
|
|
43
|
+
for (const key of [
|
|
44
|
+
'schemaVersion',
|
|
45
|
+
'packageVersion',
|
|
46
|
+
'platform',
|
|
47
|
+
'arch',
|
|
48
|
+
'pluginSourceSha256',
|
|
49
|
+
'ttscVersion',
|
|
50
|
+
'typescriptVersion',
|
|
51
|
+
'binaryAsset'
|
|
52
|
+
]) {
|
|
53
|
+
if (manifest[key] !== expected[key]) throw new Error(`manifest ${key} mismatch: expected ${expected[key]}, received ${manifest[key]}`);
|
|
54
|
+
}
|
|
55
|
+
if (manifest.cgoEnabled !== false) throw new Error('prebuilt compiler must be produced with CGO_ENABLED=0');
|
|
56
|
+
if (!/^[a-f0-9]{64}$/.test(manifest.binarySha256)) throw new Error('manifest has an invalid binary checksum');
|
|
57
|
+
if (!Number.isSafeInteger(manifest.binarySize) || manifest.binarySize <= 0 || manifest.binarySize > MAX_BINARY_BYTES) {
|
|
58
|
+
throw new Error('manifest has an invalid binary size');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function download(location, maximumBytes, options, redirects = 0) {
|
|
63
|
+
const url = new URL(location);
|
|
64
|
+
if (url.protocol !== 'https:' && !(options.allowHttp === true && url.protocol === 'http:')) {
|
|
65
|
+
throw new Error(`refusing prebuilt compiler URL protocol ${url.protocol}`);
|
|
66
|
+
}
|
|
67
|
+
const client = url.protocol === 'https:' ? https : http;
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
const request = client.get(
|
|
70
|
+
url,
|
|
71
|
+
{
|
|
72
|
+
headers: {
|
|
73
|
+
Accept: 'application/octet-stream, application/json',
|
|
74
|
+
'User-Agent': 'ts-server-foundation-type-compiler'
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
response => {
|
|
78
|
+
if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
79
|
+
response.resume();
|
|
80
|
+
if (redirects >= MAX_REDIRECTS) return reject(new Error('too many prebuilt compiler redirects'));
|
|
81
|
+
return resolve(download(new URL(response.headers.location, url).href, maximumBytes, options, redirects + 1));
|
|
82
|
+
}
|
|
83
|
+
if (response.statusCode !== 200) {
|
|
84
|
+
response.resume();
|
|
85
|
+
return reject(new Error(`prebuilt compiler request returned HTTP ${response.statusCode}`));
|
|
86
|
+
}
|
|
87
|
+
const declaredLength = Number(response.headers['content-length']);
|
|
88
|
+
if (Number.isFinite(declaredLength) && declaredLength > maximumBytes) {
|
|
89
|
+
response.destroy();
|
|
90
|
+
return reject(new Error(`prebuilt compiler response exceeds ${maximumBytes} bytes`));
|
|
91
|
+
}
|
|
92
|
+
const chunks = [];
|
|
93
|
+
let size = 0;
|
|
94
|
+
response.on('data', chunk => {
|
|
95
|
+
size += chunk.length;
|
|
96
|
+
if (size > maximumBytes) {
|
|
97
|
+
response.destroy(new Error(`prebuilt compiler response exceeds ${maximumBytes} bytes`));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
chunks.push(chunk);
|
|
101
|
+
});
|
|
102
|
+
response.on('end', () => resolve(Buffer.concat(chunks)));
|
|
103
|
+
response.on('error', reject);
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
request.setTimeout(options.requestTimeoutMs, () => request.destroy(new Error('prebuilt compiler request timed out')));
|
|
107
|
+
request.on('error', reject);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
main().catch(error => {
|
|
112
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
113
|
+
process.exitCode = 1;
|
|
114
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"strings"
|
|
8
|
+
|
|
9
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
10
|
+
shimcore "github.com/microsoft/typescript-go/shim/core"
|
|
11
|
+
shimparser "github.com/microsoft/typescript-go/shim/parser"
|
|
12
|
+
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
const (
|
|
16
|
+
runtimeImportPlaceholderName = "__tsf_runtime_import__"
|
|
17
|
+
runtimeNamespacePlaceholderName = "__tsf_runtime_namespace__"
|
|
18
|
+
runtimeAliasPlaceholderName = "__tsf_runtime_alias__"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
type expressionTemplate struct {
|
|
22
|
+
parsed *shimast.Node
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
func parseExpressionTemplate(source string) (expressionTemplate, error) {
|
|
26
|
+
source = strings.TrimSpace(source)
|
|
27
|
+
if source == "" {
|
|
28
|
+
return expressionTemplate{}, fmt.Errorf("generated expression is empty")
|
|
29
|
+
}
|
|
30
|
+
fileName := filepath.ToSlash(filepath.Join(os.TempDir(), "tsf-generated-metadata-expression.ts"))
|
|
31
|
+
file := shimparser.ParseSourceFile(
|
|
32
|
+
shimast.SourceFileParseOptions{FileName: fileName},
|
|
33
|
+
"("+source+");",
|
|
34
|
+
shimcore.ScriptKindTS,
|
|
35
|
+
)
|
|
36
|
+
if file == nil {
|
|
37
|
+
return expressionTemplate{}, fmt.Errorf("generated expression did not parse")
|
|
38
|
+
}
|
|
39
|
+
if diagnostics := file.Diagnostics(); len(diagnostics) != 0 {
|
|
40
|
+
return expressionTemplate{}, fmt.Errorf("generated expression has %d parse diagnostic(s)", len(diagnostics))
|
|
41
|
+
}
|
|
42
|
+
if file.Statements == nil || len(file.Statements.Nodes) != 1 {
|
|
43
|
+
return expressionTemplate{}, fmt.Errorf("generated expression did not parse as one statement")
|
|
44
|
+
}
|
|
45
|
+
statement := file.Statements.Nodes[0]
|
|
46
|
+
if statement == nil || statement.Kind != shimast.KindExpressionStatement || statement.AsExpressionStatement().Expression == nil {
|
|
47
|
+
return expressionTemplate{}, fmt.Errorf("generated expression did not parse as an expression statement")
|
|
48
|
+
}
|
|
49
|
+
parsed := statement.AsExpressionStatement().Expression.AsNode()
|
|
50
|
+
if err := validateRuntimePlaceholders(parsed); err != nil {
|
|
51
|
+
return expressionTemplate{}, err
|
|
52
|
+
}
|
|
53
|
+
return expressionTemplate{parsed: parsed}, nil
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func validateRuntimePlaceholders(node *shimast.Node) error {
|
|
57
|
+
var validationErr error
|
|
58
|
+
var walk func(*shimast.Node)
|
|
59
|
+
walk = func(current *shimast.Node) {
|
|
60
|
+
if current == nil || validationErr != nil {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
if current.Kind == shimast.KindCallExpression {
|
|
64
|
+
call := current.AsCallExpression()
|
|
65
|
+
if call != nil && call.Expression != nil && call.Expression.Kind == shimast.KindIdentifier {
|
|
66
|
+
name := call.Expression.Text()
|
|
67
|
+
expected := -1
|
|
68
|
+
switch name {
|
|
69
|
+
case runtimeImportPlaceholderName:
|
|
70
|
+
expected = 3
|
|
71
|
+
case runtimeNamespacePlaceholderName:
|
|
72
|
+
expected = 2
|
|
73
|
+
case runtimeAliasPlaceholderName:
|
|
74
|
+
expected = 3
|
|
75
|
+
}
|
|
76
|
+
if expected >= 0 {
|
|
77
|
+
if call.Arguments == nil || len(call.Arguments.Nodes) != expected {
|
|
78
|
+
validationErr = fmt.Errorf("%s requires %d string arguments", name, expected)
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
for _, argument := range call.Arguments.Nodes {
|
|
82
|
+
if argument == nil || !shimast.IsStringLiteral(argument) {
|
|
83
|
+
validationErr = fmt.Errorf("%s arguments must be string literals", name)
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
current.ForEachChild(func(child *shimast.Node) bool {
|
|
91
|
+
walk(child)
|
|
92
|
+
return validationErr != nil
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
walk(node)
|
|
96
|
+
return validationErr
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
func (template expressionTemplate) materialize(ec *shimprinter.EmitContext, imports *astImportRegistry) *shimast.Node {
|
|
100
|
+
if template.parsed == nil {
|
|
101
|
+
panic("tsf metadata compiler: nil expression template")
|
|
102
|
+
}
|
|
103
|
+
cloned := ec.Factory.DeepCloneNode(template.parsed)
|
|
104
|
+
var visitor *shimast.NodeVisitor
|
|
105
|
+
visitor = ec.NewNodeVisitor(func(node *shimast.Node) *shimast.Node {
|
|
106
|
+
if node == nil {
|
|
107
|
+
return nil
|
|
108
|
+
}
|
|
109
|
+
if replacement := materializeRuntimePlaceholder(node, imports); replacement != nil {
|
|
110
|
+
return replacement
|
|
111
|
+
}
|
|
112
|
+
return visitor.VisitEachChild(node)
|
|
113
|
+
})
|
|
114
|
+
result := visitor.VisitNode(cloned)
|
|
115
|
+
if containsRuntimePlaceholder(result) {
|
|
116
|
+
panic("tsf metadata compiler: runtime placeholder survived AST materialization")
|
|
117
|
+
}
|
|
118
|
+
return result
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
func materializeRuntimePlaceholder(node *shimast.Node, imports *astImportRegistry) *shimast.Node {
|
|
122
|
+
if node.Kind != shimast.KindCallExpression {
|
|
123
|
+
return nil
|
|
124
|
+
}
|
|
125
|
+
call := node.AsCallExpression()
|
|
126
|
+
if call == nil || call.Expression == nil || call.Expression.Kind != shimast.KindIdentifier || call.Arguments == nil {
|
|
127
|
+
return nil
|
|
128
|
+
}
|
|
129
|
+
args := call.Arguments.Nodes
|
|
130
|
+
switch call.Expression.Text() {
|
|
131
|
+
case runtimeNamespacePlaceholderName:
|
|
132
|
+
if len(args) == 2 && shimast.IsStringLiteral(args[0]) && shimast.IsStringLiteral(args[1]) {
|
|
133
|
+
return imports.namespace(args[0].Text(), args[1].Text())
|
|
134
|
+
}
|
|
135
|
+
case runtimeImportPlaceholderName:
|
|
136
|
+
if len(args) == 3 && shimast.IsStringLiteral(args[0]) && shimast.IsStringLiteral(args[1]) && shimast.IsStringLiteral(args[2]) {
|
|
137
|
+
return imports.member(args[0].Text(), args[1].Text(), args[2].Text())
|
|
138
|
+
}
|
|
139
|
+
case runtimeAliasPlaceholderName:
|
|
140
|
+
if len(args) == 3 && shimast.IsStringLiteral(args[0]) && shimast.IsStringLiteral(args[1]) && shimast.IsStringLiteral(args[2]) {
|
|
141
|
+
arguments := []*shimast.Node{}
|
|
142
|
+
if imports.commonJS {
|
|
143
|
+
arguments = append(arguments,
|
|
144
|
+
imports.ec.Factory.NewIdentifier("require"),
|
|
145
|
+
imports.ec.Factory.NewStringLiteral(imports.resolvedSpecifier(args[0].Text(), ""), shimast.TokenFlagsNone),
|
|
146
|
+
)
|
|
147
|
+
} else {
|
|
148
|
+
arguments = append(arguments, imports.optionalModuleLoader(args[0].Text(), ""))
|
|
149
|
+
}
|
|
150
|
+
arguments = append(arguments,
|
|
151
|
+
imports.ec.Factory.NewStringLiteral(args[1].Text(), shimast.TokenFlagsNone),
|
|
152
|
+
imports.ec.Factory.NewStringLiteral(args[2].Text(), shimast.TokenFlagsNone),
|
|
153
|
+
)
|
|
154
|
+
return imports.ec.Factory.NewCallExpression(
|
|
155
|
+
imports.staticMember(compactMetadataRuntimeSpec, compactMetadataAliasResolverName),
|
|
156
|
+
nil,
|
|
157
|
+
nil,
|
|
158
|
+
imports.ec.Factory.NewNodeList(arguments),
|
|
159
|
+
shimast.NodeFlagsNone,
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return nil
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
func containsRuntimePlaceholder(node *shimast.Node) bool {
|
|
167
|
+
found := false
|
|
168
|
+
var walk func(*shimast.Node)
|
|
169
|
+
walk = func(current *shimast.Node) {
|
|
170
|
+
if current == nil || found {
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
if current.Kind == shimast.KindIdentifier {
|
|
174
|
+
name := current.Text()
|
|
175
|
+
if name == runtimeImportPlaceholderName || name == runtimeNamespacePlaceholderName || name == runtimeAliasPlaceholderName {
|
|
176
|
+
found = true
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
current.ForEachChild(func(child *shimast.Node) bool {
|
|
181
|
+
walk(child)
|
|
182
|
+
return found
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
walk(node)
|
|
186
|
+
return found
|
|
187
|
+
}
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"strconv"
|
|
6
|
+
"strings"
|
|
7
|
+
|
|
8
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
func resolveImport(fromFile string, spec string, reg *registry) *fileInfo {
|
|
12
|
+
if !strings.HasPrefix(spec, ".") {
|
|
13
|
+
if spec == reflectionPackageSpec {
|
|
14
|
+
return resolveReflectionPackageImport(reg)
|
|
15
|
+
}
|
|
16
|
+
return nil
|
|
17
|
+
}
|
|
18
|
+
base := filepath.Clean(filepath.Join(filepath.Dir(fromFile), spec))
|
|
19
|
+
ext := strings.ToLower(filepath.Ext(base))
|
|
20
|
+
withoutExt := strings.TrimSuffix(base, filepath.Ext(base))
|
|
21
|
+
candidates := []string{}
|
|
22
|
+
switch ext {
|
|
23
|
+
case ".mjs":
|
|
24
|
+
candidates = append(candidates, moduleKey(withoutExt+".mts"), moduleKey(withoutExt+".d.mts"))
|
|
25
|
+
case ".cjs":
|
|
26
|
+
candidates = append(candidates, moduleKey(withoutExt+".cts"), moduleKey(withoutExt+".d.cts"))
|
|
27
|
+
case ".js":
|
|
28
|
+
candidates = append(candidates, moduleKey(withoutExt+".ts"), moduleKey(withoutExt+".tsx"), moduleKey(withoutExt+".d.ts"))
|
|
29
|
+
default:
|
|
30
|
+
candidates = append(candidates,
|
|
31
|
+
moduleKey(base),
|
|
32
|
+
moduleKey(base+".ts"),
|
|
33
|
+
moduleKey(base+".tsx"),
|
|
34
|
+
moduleKey(base+".mts"),
|
|
35
|
+
moduleKey(base+".cts"),
|
|
36
|
+
moduleKey(base+".d.ts"),
|
|
37
|
+
moduleKey(base+".d.mts"),
|
|
38
|
+
moduleKey(base+".d.cts"),
|
|
39
|
+
moduleKey(filepath.Join(base, "index.ts")),
|
|
40
|
+
moduleKey(filepath.Join(base, "index.mts")),
|
|
41
|
+
moduleKey(filepath.Join(base, "index.cts")),
|
|
42
|
+
moduleKey(filepath.Join(base, "index.d.ts")),
|
|
43
|
+
moduleKey(filepath.Join(base, "index.d.mts")),
|
|
44
|
+
moduleKey(filepath.Join(base, "index.d.cts")),
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
for _, candidate := range candidates {
|
|
48
|
+
if info := reg.byPath[candidate]; info != nil {
|
|
49
|
+
return info
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return nil
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
func resolveReflectionPackageImport(reg *registry) *fileInfo {
|
|
56
|
+
for _, info := range reg.files {
|
|
57
|
+
fileName := filepath.ToSlash(info.file.FileName())
|
|
58
|
+
if strings.HasSuffix(fileName, "/@zyno-io/ts-reflection/dist/index.d.ts") ||
|
|
59
|
+
strings.HasSuffix(fileName, "/packages/reflection/dist/index.d.ts") {
|
|
60
|
+
return info
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return nil
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
func classFromNode(info *fileInfo, node *shimast.Node) *classInfo {
|
|
67
|
+
file := info.file
|
|
68
|
+
nameNode := node.Name()
|
|
69
|
+
if nameNode == nil {
|
|
70
|
+
return nil
|
|
71
|
+
}
|
|
72
|
+
class := &classInfo{
|
|
73
|
+
name: nameNode.Text(),
|
|
74
|
+
pos: node.Pos(),
|
|
75
|
+
end: node.End(),
|
|
76
|
+
ambient: node.ModifierFlags()&shimast.ModifierFlagsAmbient != 0 || node.Flags&shimast.NodeFlagsAmbient != 0,
|
|
77
|
+
decoratedMethodsOnly: info.decoratedMethodsOnly,
|
|
78
|
+
}
|
|
79
|
+
for _, member := range node.Members() {
|
|
80
|
+
if member.ModifierFlags()&shimast.ModifierFlagsPrivate != 0 {
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
switch member.Kind {
|
|
84
|
+
case shimast.KindPropertyDeclaration:
|
|
85
|
+
if isStaticMember(file, member) {
|
|
86
|
+
continue
|
|
87
|
+
}
|
|
88
|
+
name := memberName(file, member)
|
|
89
|
+
if name == "" {
|
|
90
|
+
continue
|
|
91
|
+
}
|
|
92
|
+
class.properties = append(class.properties, propertyInfo{
|
|
93
|
+
name: name,
|
|
94
|
+
typeText: nodeText(file, member.Type()),
|
|
95
|
+
typeNode: member.Type(),
|
|
96
|
+
optional: member.QuestionToken() != nil,
|
|
97
|
+
})
|
|
98
|
+
case shimast.KindMethodDeclaration:
|
|
99
|
+
name := memberName(file, member)
|
|
100
|
+
if name == "" {
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
preferTypia := isHttpRouteMethod(info, member)
|
|
104
|
+
method := methodInfo{
|
|
105
|
+
name: name,
|
|
106
|
+
description: jsDocDescription(file, member),
|
|
107
|
+
typeParams: typeParameterNames(member),
|
|
108
|
+
returnType: nodeText(file, member.Type()),
|
|
109
|
+
returnTypeNode: member.Type(),
|
|
110
|
+
preferTypia: preferTypia,
|
|
111
|
+
decorated: len(member.Decorators()) != 0,
|
|
112
|
+
params: paramsFromNode(file, member),
|
|
113
|
+
}
|
|
114
|
+
if isStaticMember(file, member) {
|
|
115
|
+
class.staticMethods = append(class.staticMethods, method)
|
|
116
|
+
} else {
|
|
117
|
+
class.methods = append(class.methods, method)
|
|
118
|
+
}
|
|
119
|
+
case shimast.KindConstructor:
|
|
120
|
+
class.ctor = paramsFromNode(file, member)
|
|
121
|
+
class.hasCtor = true
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return class
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
func functionFromNode(file *shimast.SourceFile, node *shimast.Node) functionInfo {
|
|
128
|
+
nameNode := node.Name()
|
|
129
|
+
if nameNode == nil {
|
|
130
|
+
return functionInfo{}
|
|
131
|
+
}
|
|
132
|
+
return functionInfo{
|
|
133
|
+
name: nameNode.Text(),
|
|
134
|
+
typeParams: typeParameterNames(node),
|
|
135
|
+
params: paramsFromNode(file, node),
|
|
136
|
+
pos: node.Pos(),
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
func typeParameterNames(node *shimast.Node) []string {
|
|
141
|
+
params := []string{}
|
|
142
|
+
for _, param := range node.TypeParameters() {
|
|
143
|
+
if param.Name() != nil {
|
|
144
|
+
params = append(params, param.Name().Text())
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return params
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
func isHttpRouteMethod(info *fileInfo, member *shimast.Node) bool {
|
|
151
|
+
for _, decorator := range member.Decorators() {
|
|
152
|
+
if decorator.Kind != shimast.KindDecorator {
|
|
153
|
+
continue
|
|
154
|
+
}
|
|
155
|
+
if isHttpRouteDecoratorExpression(info, decorator.AsDecorator().Expression.AsNode()) {
|
|
156
|
+
return true
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return false
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
func isHttpRouteDecoratorExpression(info *fileInfo, expr *shimast.Node) bool {
|
|
163
|
+
if expr == nil {
|
|
164
|
+
return false
|
|
165
|
+
}
|
|
166
|
+
switch expr.Kind {
|
|
167
|
+
case shimast.KindParenthesizedExpression:
|
|
168
|
+
return isHttpRouteDecoratorExpression(info, expr.Expression())
|
|
169
|
+
case shimast.KindCallExpression:
|
|
170
|
+
callee := expr.Expression()
|
|
171
|
+
if isHttpRouteCallee(info, callee) {
|
|
172
|
+
return true
|
|
173
|
+
}
|
|
174
|
+
return isHttpRouteDecoratorExpression(info, callee)
|
|
175
|
+
case shimast.KindPropertyAccessExpression:
|
|
176
|
+
if isHttpRouteCallee(info, expr) {
|
|
177
|
+
return true
|
|
178
|
+
}
|
|
179
|
+
return isHttpRouteDecoratorExpression(info, expr.Expression())
|
|
180
|
+
default:
|
|
181
|
+
return false
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
func isHttpRouteCallee(info *fileInfo, callee *shimast.Node) bool {
|
|
186
|
+
if callee == nil {
|
|
187
|
+
return false
|
|
188
|
+
}
|
|
189
|
+
switch callee.Kind {
|
|
190
|
+
case shimast.KindIdentifier:
|
|
191
|
+
name := callee.Text()
|
|
192
|
+
return isHttpVerb(name) && isDirectHttpVerbImport(info, name)
|
|
193
|
+
case shimast.KindPropertyAccessExpression:
|
|
194
|
+
access := callee.AsPropertyAccessExpression()
|
|
195
|
+
name := access.Name()
|
|
196
|
+
return name != nil && isHttpVerb(name.Text()) && isHttpNamespaceExpression(info, access.Expression.AsNode())
|
|
197
|
+
default:
|
|
198
|
+
return false
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
func isHttpNamespaceExpression(info *fileInfo, expr *shimast.Node) bool {
|
|
203
|
+
for expr != nil && expr.Kind == shimast.KindParenthesizedExpression {
|
|
204
|
+
expr = expr.Expression()
|
|
205
|
+
}
|
|
206
|
+
if expr == nil || expr.Kind != shimast.KindIdentifier {
|
|
207
|
+
return false
|
|
208
|
+
}
|
|
209
|
+
return isHttpNamespaceImport(info, expr.Text())
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
func isHttpNamespaceImport(info *fileInfo, name string) bool {
|
|
213
|
+
if name != "http" {
|
|
214
|
+
return false
|
|
215
|
+
}
|
|
216
|
+
ref, ok := info.imports[name]
|
|
217
|
+
return ok && ref.exportName == "http" && isFoundationHttpImport(ref)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
func isDirectHttpVerbImport(info *fileInfo, name string) bool {
|
|
221
|
+
ref, ok := info.imports[name]
|
|
222
|
+
return ok && ref.exportName == name && isFoundationHttpImport(ref)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
func isFoundationHttpImport(ref importRef) bool {
|
|
226
|
+
spec := filepath.ToSlash(ref.spec)
|
|
227
|
+
source := filepath.ToSlash(ref.source)
|
|
228
|
+
if strings.HasSuffix(source, "/src/index") ||
|
|
229
|
+
strings.HasSuffix(source, "/src/http") ||
|
|
230
|
+
strings.HasSuffix(source, "/src/http/index") ||
|
|
231
|
+
strings.HasSuffix(source, "/src/http/decorators") {
|
|
232
|
+
return true
|
|
233
|
+
}
|
|
234
|
+
return spec == foundationPackageSpec
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
func isHttpVerb(name string) bool {
|
|
238
|
+
switch name {
|
|
239
|
+
case "GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD":
|
|
240
|
+
return true
|
|
241
|
+
default:
|
|
242
|
+
return false
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
func isStaticMember(file *shimast.SourceFile, member *shimast.Node) bool {
|
|
247
|
+
return member.ModifierFlags()&shimast.ModifierFlagsStatic != 0
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
func memberName(file *shimast.SourceFile, member *shimast.Node) string {
|
|
251
|
+
name := member.Name()
|
|
252
|
+
if name == nil {
|
|
253
|
+
return ""
|
|
254
|
+
}
|
|
255
|
+
text := strings.TrimSpace(stripTypeComments(nodeText(file, name)))
|
|
256
|
+
if text == "" || strings.HasPrefix(text, "[") || strings.HasPrefix(text, "{") {
|
|
257
|
+
return ""
|
|
258
|
+
}
|
|
259
|
+
if len(text) >= 2 {
|
|
260
|
+
quote := text[0]
|
|
261
|
+
if (quote == '\'' || quote == '"' || quote == '`') && text[len(text)-1] == quote {
|
|
262
|
+
unquoted, err := strconv.Unquote(text)
|
|
263
|
+
if err == nil {
|
|
264
|
+
return unquoted
|
|
265
|
+
}
|
|
266
|
+
return strings.Trim(text, "'\"`")
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return text
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
func paramsFromNode(file *shimast.SourceFile, node *shimast.Node) []paramInfo {
|
|
273
|
+
params := []paramInfo{}
|
|
274
|
+
for _, param := range node.Parameters() {
|
|
275
|
+
name := memberName(file, param)
|
|
276
|
+
if name == "" {
|
|
277
|
+
name = "arg"
|
|
278
|
+
}
|
|
279
|
+
params = append(params, paramInfo{
|
|
280
|
+
name: name,
|
|
281
|
+
typeText: nodeText(file, param.Type()),
|
|
282
|
+
typeNode: param.Type(),
|
|
283
|
+
optional: param.QuestionToken() != nil || param.Initializer() != nil,
|
|
284
|
+
hasDefault: param.Initializer() != nil,
|
|
285
|
+
})
|
|
286
|
+
}
|
|
287
|
+
return params
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
func nodeText(file *shimast.SourceFile, node *shimast.Node) string {
|
|
291
|
+
if node == nil {
|
|
292
|
+
return "unknown"
|
|
293
|
+
}
|
|
294
|
+
return strings.TrimSpace(stripTypeComments(file.Text()[node.Pos():node.End()]))
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
func jsDocDescription(file *shimast.SourceFile, node *shimast.Node) string {
|
|
298
|
+
docs := node.JSDoc(file)
|
|
299
|
+
if len(docs) == 0 {
|
|
300
|
+
return ""
|
|
301
|
+
}
|
|
302
|
+
doc := docs[len(docs)-1]
|
|
303
|
+
return cleanJsDocDescription(file.Text()[doc.Pos():doc.End()])
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
func cleanJsDocDescription(raw string) string {
|
|
307
|
+
raw = strings.TrimSpace(raw)
|
|
308
|
+
raw = strings.TrimPrefix(raw, "/**")
|
|
309
|
+
raw = strings.TrimSuffix(raw, "*/")
|
|
310
|
+
lines := strings.Split(raw, "\n")
|
|
311
|
+
parts := []string{}
|
|
312
|
+
for _, line := range lines {
|
|
313
|
+
line = strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(line), "*"))
|
|
314
|
+
if strings.HasPrefix(line, "@") {
|
|
315
|
+
break
|
|
316
|
+
}
|
|
317
|
+
if line == "" {
|
|
318
|
+
if len(parts) > 0 {
|
|
319
|
+
break
|
|
320
|
+
}
|
|
321
|
+
continue
|
|
322
|
+
}
|
|
323
|
+
parts = append(parts, line)
|
|
324
|
+
}
|
|
325
|
+
return strings.Join(parts, " ")
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
func classMetadata(info *fileInfo, reg *registry, class *classInfo, typeRef func(string) string) string {
|
|
329
|
+
props := []string{}
|
|
330
|
+
for _, prop := range class.properties {
|
|
331
|
+
items := []string{
|
|
332
|
+
"name: " + quote(prop.name),
|
|
333
|
+
"type: " + referMetadataType(typeRef, cachedTypeExpr(info, reg, prop.typeText, prop.typeNode, class.pos, prop.metadataText)),
|
|
334
|
+
"optional: " + boolLit(prop.optional),
|
|
335
|
+
}
|
|
336
|
+
flags := collectFlags(info, reg, prop.typeText)
|
|
337
|
+
if flags["primaryKey"] != "" {
|
|
338
|
+
items = append(items, "primaryKey: true")
|
|
339
|
+
}
|
|
340
|
+
if flags["autoIncrement"] != "" {
|
|
341
|
+
items = append(items, "autoIncrement: true")
|
|
342
|
+
}
|
|
343
|
+
for _, key := range []string{"reference", "index", "unique"} {
|
|
344
|
+
if value := flags[key]; value != "" {
|
|
345
|
+
items = append(items, key+": "+value)
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
props = append(props, "{"+strings.Join(items, ", ")+"}")
|
|
349
|
+
}
|
|
350
|
+
methods := []string{}
|
|
351
|
+
for _, method := range class.methods {
|
|
352
|
+
if class.decoratedMethodsOnly && !method.decorated {
|
|
353
|
+
continue
|
|
354
|
+
}
|
|
355
|
+
items := []string{
|
|
356
|
+
"name: " + quote(method.name),
|
|
357
|
+
"parameters: " + paramsExpr(info, reg, method.params, class.pos, typeRef),
|
|
358
|
+
"returnType: " + referMetadataType(typeRef, cachedTypeExpr(info, reg, method.returnType, method.returnTypeNode, class.pos, method.returnMetadataText)),
|
|
359
|
+
}
|
|
360
|
+
if method.description != "" {
|
|
361
|
+
items = append(items, "description: "+quote(method.description))
|
|
362
|
+
}
|
|
363
|
+
methods = append(methods, "{"+strings.Join(items, ", ")+"}")
|
|
364
|
+
}
|
|
365
|
+
return "{kind: 16, name: " + quote(class.name) +
|
|
366
|
+
", typeName: " + quote(class.name) +
|
|
367
|
+
", classType: () => " + class.name +
|
|
368
|
+
", properties: [" + strings.Join(props, ", ") + "]" +
|
|
369
|
+
", methods: [" + strings.Join(methods, ", ") + "]" +
|
|
370
|
+
", hasConstructor: " + boolLit(class.hasCtor) +
|
|
371
|
+
", constructorParameters: " + paramsExpr(info, reg, class.ctor, class.pos, typeRef) + "}"
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
func paramsExpr(info *fileInfo, reg *registry, params []paramInfo, pos int, typeRef func(string) string) string {
|
|
375
|
+
out := []string{}
|
|
376
|
+
for _, param := range params {
|
|
377
|
+
typeExpr := referMetadataType(typeRef, cachedTypeExpr(info, reg, param.typeText, param.typeNode, pos, param.metadataText))
|
|
378
|
+
out = append(out, "{name: "+quote(param.name)+", type: "+typeExpr+", optional: "+boolLit(param.optional)+", default: "+boolLit(param.hasDefault)+"}")
|
|
379
|
+
}
|
|
380
|
+
return "[" + strings.Join(out, ", ") + "]"
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
func referMetadataType(typeRef func(string) string, expr string) string {
|
|
384
|
+
if typeRef == nil {
|
|
385
|
+
return expr
|
|
386
|
+
}
|
|
387
|
+
return typeRef(expr)
|
|
388
|
+
}
|