typia 13.0.0-dev.20260510 → 13.0.0-dev.20260514
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 +36 -17
- package/lib/executable/TypiaGenerateWizard2.mjs +28 -7
- package/lib/executable/TypiaGenerateWizard2.mjs.map +1 -1
- package/lib/executable/generate/ttsc.d.ts +1 -11
- package/lib/executable/generate/ttsc.js +1 -0
- package/lib/executable/generate/ttsc.js.map +1 -1
- package/lib/executable/generate/ttsc2.mjs +4 -0
- package/lib/executable/generate/ttsc2.mjs.map +1 -1
- package/lib/executable/typia.d.ts +1 -4
- package/lib/executable/typia.js +4 -36
- package/lib/executable/typia.js.map +1 -1
- package/lib/executable/typia2.mjs +5 -39
- package/lib/executable/typia2.mjs.map +1 -1
- package/native/adapter/adapter.go +14 -4
- package/native/adapter/cleanup.go +42 -0
- package/native/adapter/imports.go +12 -1
- package/native/cmd/ttsc-typia/main.go +2 -0
- package/native/cmd/ttsc-typia/main_wasm.go +226 -0
- package/native/cmd/ttsc-typia/transform.go +1 -40
- package/native/core/factories/MetadataFactory.go +480 -462
- package/native/core/factories/internal/metadata/IMetadataIteratorProps.go +42 -35
- package/native/core/factories/internal/metadata/MetadataHelper.go +209 -176
- package/native/core/factories/internal/metadata/iterate_metadata_comment_tags.go +26 -12
- package/native/go.work +22 -0
- package/native/internal/printer/printer.go +202 -202
- package/native/transform/CallExpressionTransformer.go +26 -1
- package/package.json +4 -4
- package/src/executable/typia.ts +1 -1
|
@@ -31,6 +31,48 @@ func CleanupTransformedText(text string) string {
|
|
|
31
31
|
return injectRuntimeImports(text)
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
func CleanupTypeScriptTransformText(text string) string {
|
|
35
|
+
text = CleanupTransformedText(text)
|
|
36
|
+
text = normalizeParenthesizedTypeAnnotations(text)
|
|
37
|
+
text = regexp.MustCompile(`(?m)^import type \{([^{}\n]+)\} from`).ReplaceAllStringFunc(text, func(line string) string {
|
|
38
|
+
return regexp.MustCompile(`^import type \{\s*([^{}\n]+?)\s*\} from`).ReplaceAllString(line, "import type { $1 } from")
|
|
39
|
+
})
|
|
40
|
+
text = regexp.MustCompile(`(?m)(^import [^\n]+;\n)\n+(const |let |var |export )`).ReplaceAllString(text, "$1$2")
|
|
41
|
+
text = strings.ReplaceAll(text, "=(() =>", "= (() =>")
|
|
42
|
+
text = strings.ReplaceAll(text, ": (any) =>", ": any =>")
|
|
43
|
+
text = strings.ReplaceAll(text, ": (boolean) =>", ": boolean =>")
|
|
44
|
+
text = regexp.MustCompile(`input is \(([A-Za-z_$][A-Za-z0-9_$.]*)\)`).ReplaceAllString(text, "input is $1")
|
|
45
|
+
text = strings.ReplaceAll(text, "return (success ? ", "return success ? ")
|
|
46
|
+
text = strings.ReplaceAll(text, "}) as any;", "} as any;")
|
|
47
|
+
text = strings.ReplaceAll(text, "(() => {\n const ", "(() => { const ")
|
|
48
|
+
text = strings.ReplaceAll(text, "(() => {\n let ", "(() => { let ")
|
|
49
|
+
text = strings.ReplaceAll(text, "(() => {\n return ", "(() => { return ")
|
|
50
|
+
text = strings.ReplaceAll(text, ";\n const ", "; const ")
|
|
51
|
+
text = strings.ReplaceAll(text, ";\n let ", "; let ")
|
|
52
|
+
text = strings.ReplaceAll(text, ";\n return ", "; return ")
|
|
53
|
+
text = strings.ReplaceAll(text, "\n };\n})()", "\n}; })()")
|
|
54
|
+
text = strings.ReplaceAll(text, "\n });\n})()", "\n}); })()")
|
|
55
|
+
text = strings.ReplaceAll(text, "\n }); let ", "\n}); let ")
|
|
56
|
+
text = strings.ReplaceAll(text, ";\n})()", "; })()")
|
|
57
|
+
text = strings.ReplaceAll(text, "\n ", "\n ")
|
|
58
|
+
text = regexp.MustCompile(`\n\n([A-Za-z_$][A-Za-z0-9_$]*\([^;\n]*\);?)`).ReplaceAllString(text, "\n$1")
|
|
59
|
+
trimmed := strings.TrimRight(text, " \t\r\n")
|
|
60
|
+
if strings.HasSuffix(trimmed, ")") && !strings.HasSuffix(trimmed, ";") {
|
|
61
|
+
return trimmed + ";\n"
|
|
62
|
+
}
|
|
63
|
+
if text != "" && !strings.HasSuffix(text, "\n") {
|
|
64
|
+
return text + "\n"
|
|
65
|
+
}
|
|
66
|
+
return text
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
func normalizeParenthesizedTypeAnnotations(text string) string {
|
|
70
|
+
typeAtom := `([A-Za-z_$][A-Za-z0-9_$.]*(<[^()\n;{}]*>)?)`
|
|
71
|
+
text = regexp.MustCompile(`: \(`+typeAtom+`\)(\s*=>)`).ReplaceAllString(text, ": $1$3")
|
|
72
|
+
text = regexp.MustCompile(`\| \((null|undefined)\)`).ReplaceAllString(text, "| $1")
|
|
73
|
+
return text
|
|
74
|
+
}
|
|
75
|
+
|
|
34
76
|
type unusedImportPattern struct {
|
|
35
77
|
regex *regexp.Regexp
|
|
36
78
|
alias func(string) string
|
|
@@ -4,15 +4,25 @@ import (
|
|
|
4
4
|
"path/filepath"
|
|
5
5
|
"strconv"
|
|
6
6
|
"strings"
|
|
7
|
+
"sync"
|
|
7
8
|
"unicode"
|
|
8
9
|
|
|
9
10
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
10
11
|
)
|
|
11
12
|
|
|
13
|
+
type commonJSImportIdentifierSubstitutionsCacheEntry struct {
|
|
14
|
+
value map[string]string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
var commonJSImportIdentifierSubstitutionsCache sync.Map
|
|
18
|
+
|
|
12
19
|
func commonJSImportIdentifierSubstitutions(file *shimast.SourceFile) map[string]string {
|
|
13
20
|
if file == nil || file.Statements == nil {
|
|
14
21
|
return nil
|
|
15
22
|
}
|
|
23
|
+
if cached, ok := commonJSImportIdentifierSubstitutionsCache.Load(file); ok {
|
|
24
|
+
return cached.(commonJSImportIdentifierSubstitutionsCacheEntry).value
|
|
25
|
+
}
|
|
16
26
|
output := map[string]string{}
|
|
17
27
|
counts := map[string]int{}
|
|
18
28
|
for _, stmt := range file.Statements.Nodes {
|
|
@@ -61,8 +71,9 @@ func commonJSImportIdentifierSubstitutions(file *shimast.SourceFile) map[string]
|
|
|
61
71
|
}
|
|
62
72
|
}
|
|
63
73
|
if len(output) == 0 {
|
|
64
|
-
|
|
74
|
+
output = nil
|
|
65
75
|
}
|
|
76
|
+
commonJSImportIdentifierSubstitutionsCache.Store(file, commonJSImportIdentifierSubstitutionsCacheEntry{value: output})
|
|
66
77
|
return output
|
|
67
78
|
}
|
|
68
79
|
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
//go:build js && wasm
|
|
2
|
+
|
|
3
|
+
package main
|
|
4
|
+
|
|
5
|
+
import (
|
|
6
|
+
"bytes"
|
|
7
|
+
"fmt"
|
|
8
|
+
"io"
|
|
9
|
+
"os"
|
|
10
|
+
"runtime"
|
|
11
|
+
"syscall/js"
|
|
12
|
+
"time"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
// Reuse the same package-level identifiers the native CLI declares. Under
|
|
16
|
+
// `js && wasm`, the native `main.go` is excluded, so we re-declare them here.
|
|
17
|
+
var (
|
|
18
|
+
version = "0.1.0-dev"
|
|
19
|
+
commit = "dev"
|
|
20
|
+
date = "unknown"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
var (
|
|
24
|
+
stdout io.Writer = os.Stdout
|
|
25
|
+
stderr io.Writer = os.Stderr
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
// main keeps the Go runtime alive forever after registering the JS entry
|
|
29
|
+
// points. JS code calls `globalThis.ttscTypia.transform({...})` repeatedly;
|
|
30
|
+
// each call reuses the same Go process (no re-instantiation cost).
|
|
31
|
+
func main() {
|
|
32
|
+
api := map[string]any{
|
|
33
|
+
"transform": js.FuncOf(jsTransform),
|
|
34
|
+
"build": js.FuncOf(jsBuild),
|
|
35
|
+
"version": js.FuncOf(jsVersion),
|
|
36
|
+
}
|
|
37
|
+
js.Global().Set("ttscTypia", js.ValueOf(api))
|
|
38
|
+
|
|
39
|
+
// Signal readiness so the JS host can resolve a "ready" promise.
|
|
40
|
+
if ready := js.Global().Get("ttscTypiaReady"); ready.Type() == js.TypeFunction {
|
|
41
|
+
ready.Invoke()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// A perpetual idle goroutine prevents Go's wasm deadlock detector from
|
|
45
|
+
// tripping while syscall.fsCall callbacks are in flight. `select {}` alone
|
|
46
|
+
// is wrongly classified as "all goroutines asleep" the moment the FS path
|
|
47
|
+
// hands the request off to JS.
|
|
48
|
+
go func() {
|
|
49
|
+
for {
|
|
50
|
+
time.Sleep(time.Hour)
|
|
51
|
+
}
|
|
52
|
+
}()
|
|
53
|
+
select {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// jsVersion returns the build metadata for this wasm binary.
|
|
57
|
+
func jsVersion(this js.Value, args []js.Value) any {
|
|
58
|
+
return js.ValueOf(map[string]any{
|
|
59
|
+
"version": version,
|
|
60
|
+
"commit": commit,
|
|
61
|
+
"date": date,
|
|
62
|
+
"go": runtime.Version(),
|
|
63
|
+
"goos": runtime.GOOS,
|
|
64
|
+
"goarch": runtime.GOARCH,
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// jsTransform runs the project transform pipeline on a single file.
|
|
69
|
+
//
|
|
70
|
+
// JS input shape:
|
|
71
|
+
// {
|
|
72
|
+
// cwd: string, // absolute virtual path
|
|
73
|
+
// tsconfig: string, // absolute path to tsconfig.json
|
|
74
|
+
// file?: string, // absolute path of the .ts file to rewrite. Omit
|
|
75
|
+
// // for project-wide JSON output.
|
|
76
|
+
// output?: "ts"|"js",
|
|
77
|
+
// }
|
|
78
|
+
//
|
|
79
|
+
// JS output: Promise<{ code: number, stdout: string, stderr: string }>.
|
|
80
|
+
//
|
|
81
|
+
// The handler returns a Promise so the JS event loop can drain while Go
|
|
82
|
+
// services its own asynchronous file-system callbacks. Returning a synchronous
|
|
83
|
+
// value would re-enter JS and deadlock on `fs.stat` callbacks.
|
|
84
|
+
//
|
|
85
|
+
// Caller is responsible for populating an in-memory filesystem (via the
|
|
86
|
+
// `globalThis.fs` shim picked up by wasm_exec.js) with every path referenced
|
|
87
|
+
// here before invoking transform.
|
|
88
|
+
func jsTransform(this js.Value, args []js.Value) any {
|
|
89
|
+
if len(args) == 0 || args[0].Type() != js.TypeObject {
|
|
90
|
+
return errorPromise(2, "ttsc-typia: transform requires an options object")
|
|
91
|
+
}
|
|
92
|
+
argv := transformArgs(args[0])
|
|
93
|
+
return makePromise(func() any {
|
|
94
|
+
return runWithCapturedIO(func() int { return runTransform(argv) })
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// jsBuild runs the project build pipeline. JS input mirrors jsTransform but
|
|
99
|
+
// has no `file`/`output`. Returns a Promise.
|
|
100
|
+
func jsBuild(this js.Value, args []js.Value) any {
|
|
101
|
+
if len(args) == 0 || args[0].Type() != js.TypeObject {
|
|
102
|
+
return errorPromise(2, "ttsc-typia: build requires an options object")
|
|
103
|
+
}
|
|
104
|
+
argv := buildArgs(args[0])
|
|
105
|
+
return makePromise(func() any {
|
|
106
|
+
return runWithCapturedIO(func() int { return runBuild(argv) })
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// makePromise wraps a Go computation as a JS Promise. The returned object is
|
|
111
|
+
// `new Promise((resolve, reject) => { ... })`. The executor synchronously
|
|
112
|
+
// captures the callbacks; the work itself runs in a goroutine so the JS event
|
|
113
|
+
// loop can drive `fs.stat` and friends to completion before we ask Go to
|
|
114
|
+
// receive the callback.
|
|
115
|
+
func makePromise(work func() any) js.Value {
|
|
116
|
+
var executor js.Func
|
|
117
|
+
executor = js.FuncOf(func(this js.Value, args []js.Value) any {
|
|
118
|
+
resolve := args[0]
|
|
119
|
+
reject := args[1]
|
|
120
|
+
go func() {
|
|
121
|
+
defer func() {
|
|
122
|
+
if r := recover(); r != nil {
|
|
123
|
+
err := js.Global().Get("Error").New(fmt.Sprintf("%v", r))
|
|
124
|
+
reject.Invoke(err)
|
|
125
|
+
}
|
|
126
|
+
executor.Release()
|
|
127
|
+
}()
|
|
128
|
+
resolve.Invoke(work())
|
|
129
|
+
}()
|
|
130
|
+
return nil
|
|
131
|
+
})
|
|
132
|
+
return js.Global().Get("Promise").New(executor)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
func errorPromise(code int, message string) js.Value {
|
|
136
|
+
return makePromise(func() any { return errorResponse(code, message) })
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
func transformArgs(opts js.Value) []string {
|
|
140
|
+
out := []string{}
|
|
141
|
+
if v := stringProp(opts, "cwd"); v != "" {
|
|
142
|
+
out = append(out, "--cwd="+v)
|
|
143
|
+
}
|
|
144
|
+
if v := stringProp(opts, "tsconfig"); v != "" {
|
|
145
|
+
out = append(out, "--tsconfig="+v)
|
|
146
|
+
} else {
|
|
147
|
+
out = append(out, "--tsconfig=tsconfig.json")
|
|
148
|
+
}
|
|
149
|
+
if v := stringProp(opts, "file"); v != "" {
|
|
150
|
+
out = append(out, "--file="+v)
|
|
151
|
+
}
|
|
152
|
+
if v := stringProp(opts, "output"); v != "" {
|
|
153
|
+
out = append(out, "--output="+v)
|
|
154
|
+
} else {
|
|
155
|
+
out = append(out, "--output=ts")
|
|
156
|
+
}
|
|
157
|
+
return out
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
func buildArgs(opts js.Value) []string {
|
|
161
|
+
out := []string{}
|
|
162
|
+
if v := stringProp(opts, "cwd"); v != "" {
|
|
163
|
+
out = append(out, "--cwd="+v)
|
|
164
|
+
}
|
|
165
|
+
if v := stringProp(opts, "tsconfig"); v != "" {
|
|
166
|
+
out = append(out, "--tsconfig="+v)
|
|
167
|
+
} else {
|
|
168
|
+
out = append(out, "--tsconfig=tsconfig.json")
|
|
169
|
+
}
|
|
170
|
+
if outDir := stringProp(opts, "outDir"); outDir != "" {
|
|
171
|
+
out = append(out, "--outDir="+outDir)
|
|
172
|
+
}
|
|
173
|
+
if boolProp(opts, "emit") {
|
|
174
|
+
out = append(out, "--emit")
|
|
175
|
+
}
|
|
176
|
+
if boolProp(opts, "noEmit") {
|
|
177
|
+
out = append(out, "--noEmit")
|
|
178
|
+
}
|
|
179
|
+
return out
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
func runWithCapturedIO(task func() int) any {
|
|
183
|
+
// Swap the package-level writers so the inner Go functions feel like a
|
|
184
|
+
// normal CLI invocation but their output lands in JS-visible buffers.
|
|
185
|
+
// This is safe under the contract that JS calls are serialized (no
|
|
186
|
+
// concurrent invocations from JS).
|
|
187
|
+
oldOut, oldErr := stdout, stderr
|
|
188
|
+
var outBuf, errBuf bytes.Buffer
|
|
189
|
+
stdout = &outBuf
|
|
190
|
+
stderr = &errBuf
|
|
191
|
+
defer func() {
|
|
192
|
+
stdout = oldOut
|
|
193
|
+
stderr = oldErr
|
|
194
|
+
}()
|
|
195
|
+
|
|
196
|
+
code := task()
|
|
197
|
+
return js.ValueOf(map[string]any{
|
|
198
|
+
"code": code,
|
|
199
|
+
"stdout": outBuf.String(),
|
|
200
|
+
"stderr": errBuf.String(),
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
func errorResponse(code int, message string) any {
|
|
205
|
+
return js.ValueOf(map[string]any{
|
|
206
|
+
"code": code,
|
|
207
|
+
"stdout": "",
|
|
208
|
+
"stderr": fmt.Sprintf("%s\n", message),
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
func stringProp(obj js.Value, key string) string {
|
|
213
|
+
v := obj.Get(key)
|
|
214
|
+
if v.Type() != js.TypeString {
|
|
215
|
+
return ""
|
|
216
|
+
}
|
|
217
|
+
return v.String()
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
func boolProp(obj js.Value, key string) bool {
|
|
221
|
+
v := obj.Get(key)
|
|
222
|
+
if v.Type() != js.TypeBoolean {
|
|
223
|
+
return false
|
|
224
|
+
}
|
|
225
|
+
return v.Bool()
|
|
226
|
+
}
|
|
@@ -7,7 +7,6 @@ import (
|
|
|
7
7
|
"fmt"
|
|
8
8
|
"os"
|
|
9
9
|
"path/filepath"
|
|
10
|
-
"regexp"
|
|
11
10
|
"sort"
|
|
12
11
|
"strings"
|
|
13
12
|
|
|
@@ -373,45 +372,7 @@ func applySourceRewrites(source string, rewrites []transformSourceRewrite) (stri
|
|
|
373
372
|
}
|
|
374
373
|
|
|
375
374
|
func cleanupTypeScriptTransformText(text string) string {
|
|
376
|
-
|
|
377
|
-
text = normalizeParenthesizedTypeAnnotations(text)
|
|
378
|
-
text = regexp.MustCompile(`(?m)^import type \{([^{}\n]+)\} from`).ReplaceAllStringFunc(text, func(line string) string {
|
|
379
|
-
return regexp.MustCompile(`^import type \{\s*([^{}\n]+?)\s*\} from`).ReplaceAllString(line, "import type { $1 } from")
|
|
380
|
-
})
|
|
381
|
-
text = regexp.MustCompile(`(?m)(^import [^\n]+;\n)\n+(const |let |var |export )`).ReplaceAllString(text, "$1$2")
|
|
382
|
-
text = strings.ReplaceAll(text, "=(() =>", "= (() =>")
|
|
383
|
-
text = strings.ReplaceAll(text, ": (any) =>", ": any =>")
|
|
384
|
-
text = strings.ReplaceAll(text, ": (boolean) =>", ": boolean =>")
|
|
385
|
-
text = regexp.MustCompile(`input is \(([A-Za-z_$][A-Za-z0-9_$.]*)\)`).ReplaceAllString(text, "input is $1")
|
|
386
|
-
text = strings.ReplaceAll(text, "return (success ? ", "return success ? ")
|
|
387
|
-
text = strings.ReplaceAll(text, "}) as any;", "} as any;")
|
|
388
|
-
text = strings.ReplaceAll(text, "(() => {\n const ", "(() => { const ")
|
|
389
|
-
text = strings.ReplaceAll(text, "(() => {\n let ", "(() => { let ")
|
|
390
|
-
text = strings.ReplaceAll(text, "(() => {\n return ", "(() => { return ")
|
|
391
|
-
text = strings.ReplaceAll(text, ";\n const ", "; const ")
|
|
392
|
-
text = strings.ReplaceAll(text, ";\n let ", "; let ")
|
|
393
|
-
text = strings.ReplaceAll(text, ";\n return ", "; return ")
|
|
394
|
-
text = strings.ReplaceAll(text, "\n };\n})()", "\n}; })()")
|
|
395
|
-
text = strings.ReplaceAll(text, "\n });\n})()", "\n}); })()")
|
|
396
|
-
text = strings.ReplaceAll(text, "\n }); let ", "\n}); let ")
|
|
397
|
-
text = strings.ReplaceAll(text, ";\n})()", "; })()")
|
|
398
|
-
text = strings.ReplaceAll(text, "\n ", "\n ")
|
|
399
|
-
text = regexp.MustCompile(`\n\n([A-Za-z_$][A-Za-z0-9_$]*\([^;\n]*\);?)`).ReplaceAllString(text, "\n$1")
|
|
400
|
-
trimmed := strings.TrimRight(text, " \t\r\n")
|
|
401
|
-
if strings.HasSuffix(trimmed, ")") && !strings.HasSuffix(trimmed, ";") {
|
|
402
|
-
return trimmed + ";\n"
|
|
403
|
-
}
|
|
404
|
-
if text != "" && !strings.HasSuffix(text, "\n") {
|
|
405
|
-
return text + "\n"
|
|
406
|
-
}
|
|
407
|
-
return text
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
func normalizeParenthesizedTypeAnnotations(text string) string {
|
|
411
|
-
typeAtom := `([A-Za-z_$][A-Za-z0-9_$.]*(<[^()\n;{}]*>)?)`
|
|
412
|
-
text = regexp.MustCompile(`: \(`+typeAtom+`\)(\s*=>)`).ReplaceAllString(text, ": $1$3")
|
|
413
|
-
text = regexp.MustCompile(`\| \((null|undefined)\)`).ReplaceAllString(text, "| $1")
|
|
414
|
-
return text
|
|
375
|
+
return typiaadapter.CleanupTypeScriptTransformText(text)
|
|
415
376
|
}
|
|
416
377
|
|
|
417
378
|
func sourceFileKey(cwd string, file string) string {
|