@nestia/core 12.0.0-dev.20260601.1 → 12.0.0-dev.20260612.2
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/LICENSE +21 -21
- package/MIGRATION.md +169 -169
- package/README.md +93 -93
- package/lib/adaptors/McpAdaptor.d.ts +75 -0
- package/lib/adaptors/McpAdaptor.js +257 -0
- package/lib/adaptors/McpAdaptor.js.map +1 -0
- package/lib/adaptors/WebSocketAdaptor.js +4 -4
- package/lib/adaptors/WebSocketAdaptor.js.map +1 -1
- package/lib/decorators/McpRoute.d.ts +69 -0
- package/lib/decorators/McpRoute.js +58 -0
- package/lib/decorators/McpRoute.js.map +1 -0
- package/lib/decorators/TypedParam.js +4 -4
- package/lib/decorators/TypedParam.js.map +1 -1
- package/lib/decorators/TypedRoute.js +1 -1
- package/lib/decorators/TypedRoute.js.map +1 -1
- package/lib/decorators/internal/IMcpRouteReflect.d.ts +2 -0
- package/lib/decorators/internal/IMcpRouteReflect.js +3 -0
- package/lib/decorators/internal/IMcpRouteReflect.js.map +1 -0
- package/lib/decorators/internal/get_path_and_querify.js +4 -4
- package/lib/decorators/internal/get_path_and_querify.js.map +1 -1
- package/lib/decorators/internal/get_path_and_stringify.js +4 -4
- package/lib/decorators/internal/get_path_and_stringify.js.map +1 -1
- package/lib/decorators/internal/load_controller.js +34 -65
- package/lib/decorators/internal/load_controller.js.map +1 -1
- package/lib/decorators/internal/validate_request_body.js +4 -4
- package/lib/decorators/internal/validate_request_body.js.map +1 -1
- package/lib/decorators/internal/validate_request_form_data.js +4 -4
- package/lib/decorators/internal/validate_request_form_data.js.map +1 -1
- package/lib/decorators/internal/validate_request_headers.js +4 -4
- package/lib/decorators/internal/validate_request_headers.js.map +1 -1
- package/lib/decorators/internal/validate_request_query.js +4 -4
- package/lib/decorators/internal/validate_request_query.js.map +1 -1
- package/lib/module.d.ts +2 -0
- package/lib/module.js +2 -0
- package/lib/module.js.map +1 -1
- package/native/cmd/ttsc-nestia/main.go +11 -11
- package/native/go.mod +32 -32
- package/native/go.sum +54 -54
- package/native/plugin/plan.go +102 -102
- package/native/transform/ast.go +32 -32
- package/native/transform/build.go +380 -444
- package/native/transform/cleanup.go +408 -408
- package/native/transform/contributor.go +97 -68
- package/native/transform/core_querify.go +231 -227
- package/native/transform/core_transform.go +1996 -1713
- package/native/transform/core_websocket.go +115 -115
- package/native/transform/exports.go +13 -13
- package/native/transform/mcp_transform.go +414 -0
- package/native/transform/node_transform.go +357 -0
- package/native/transform/path_rewrite.go +285 -285
- package/native/transform/printer.go +244 -244
- package/native/transform/rewrite.go +668 -662
- package/native/transform/run.go +73 -73
- package/native/transform/transform.go +336 -403
- package/native/transform/typia_fast.go +352 -326
- package/native/transform/typia_replacement.go +24 -24
- package/native/transform.cjs +43 -43
- package/package.json +15 -8
- package/src/adaptors/McpAdaptor.ts +276 -0
- package/src/adaptors/WebSocketAdaptor.ts +429 -429
- package/src/decorators/DynamicModule.ts +44 -44
- package/src/decorators/EncryptedBody.ts +97 -97
- package/src/decorators/EncryptedController.ts +40 -40
- package/src/decorators/EncryptedModule.ts +98 -98
- package/src/decorators/EncryptedRoute.ts +213 -213
- package/src/decorators/HumanRoute.ts +21 -21
- package/src/decorators/McpRoute.ts +154 -0
- package/src/decorators/NoTransformConfigurationError.ts +40 -40
- package/src/decorators/PlainBody.ts +76 -76
- package/src/decorators/SwaggerCustomizer.ts +97 -97
- package/src/decorators/SwaggerExample.ts +180 -180
- package/src/decorators/TypedBody.ts +57 -57
- package/src/decorators/TypedException.ts +147 -147
- package/src/decorators/TypedFormData.ts +187 -187
- package/src/decorators/TypedHeaders.ts +66 -66
- package/src/decorators/TypedParam.ts +77 -77
- package/src/decorators/TypedQuery.ts +234 -234
- package/src/decorators/TypedRoute.ts +198 -196
- package/src/decorators/WebSocketRoute.ts +242 -242
- package/src/decorators/doNotThrowTransformError.ts +5 -5
- package/src/decorators/internal/EncryptedConstant.ts +2 -2
- package/src/decorators/internal/IMcpRouteReflect.ts +40 -0
- package/src/decorators/internal/IWebSocketRouteReflect.ts +23 -23
- package/src/decorators/internal/get_path_and_querify.ts +94 -94
- package/src/decorators/internal/get_path_and_stringify.ts +110 -110
- package/src/decorators/internal/get_text_body.ts +16 -16
- package/src/decorators/internal/headers_to_object.ts +11 -11
- package/src/decorators/internal/is_request_body_undefined.ts +12 -12
- package/src/decorators/internal/load_controller.ts +91 -76
- package/src/decorators/internal/route_error.ts +43 -43
- package/src/decorators/internal/validate_request_body.ts +64 -64
- package/src/decorators/internal/validate_request_form_data.ts +67 -67
- package/src/decorators/internal/validate_request_headers.ts +76 -76
- package/src/decorators/internal/validate_request_query.ts +83 -83
- package/src/index.ts +5 -5
- package/src/module.ts +25 -23
- package/src/options/IRequestBodyValidator.ts +20 -20
- package/src/options/IRequestFormDataProps.ts +27 -27
- package/src/options/IRequestHeadersValidator.ts +22 -22
- package/src/options/IRequestQueryValidator.ts +20 -20
- package/src/options/IResponseBodyQuerifier.ts +25 -25
- package/src/options/IResponseBodyStringifier.ts +30 -30
- package/src/transform.ts +101 -101
- package/src/typings/Creator.ts +3 -3
- package/src/typings/get-function-location.d.ts +7 -7
- package/src/utils/ArrayUtil.ts +7 -7
- package/src/utils/ExceptionManager.ts +115 -115
- package/src/utils/Singleton.ts +16 -16
- package/src/utils/SourceFinder.ts +54 -54
- package/src/utils/VersioningStrategy.ts +27 -27
- package/native/transform/cleanup_test.go +0 -76
- package/native/transform/commonjs_import_alias_test.go +0 -49
- package/native/transform/core_dispatch_test.go +0 -127
- package/native/transform/path_rewrite_test.go +0 -243
- package/native/transform/rewrite_test.go +0 -118
- package/native/transform/rewrite_unique_base_test.go +0 -48
|
@@ -1,403 +1,336 @@
|
|
|
1
|
-
package transform
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"bytes"
|
|
5
|
-
"encoding/json"
|
|
6
|
-
"flag"
|
|
7
|
-
"fmt"
|
|
8
|
-
"os"
|
|
9
|
-
"path/filepath"
|
|
10
|
-
"regexp"
|
|
11
|
-
"sort"
|
|
12
|
-
"strings"
|
|
13
|
-
|
|
14
|
-
"github.com/
|
|
15
|
-
"github.com/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
fs.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if
|
|
87
|
-
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
var
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
text = strings.ReplaceAll(text, ";\n})()", "; })()")
|
|
338
|
-
text = strings.ReplaceAll(text, "\n ", "\n ")
|
|
339
|
-
text = cleanupCollapseBlankCallPattern.ReplaceAllString(text, "\n$1")
|
|
340
|
-
trimmed := strings.TrimRight(text, " \t\r\n")
|
|
341
|
-
if strings.HasSuffix(trimmed, ")") && !strings.HasSuffix(trimmed, ";") {
|
|
342
|
-
return trimmed + ";\n"
|
|
343
|
-
}
|
|
344
|
-
if text != "" && !strings.HasSuffix(text, "\n") {
|
|
345
|
-
return text + "\n"
|
|
346
|
-
}
|
|
347
|
-
return text
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
var (
|
|
351
|
-
normalizeParenArrowTypePattern = regexp.MustCompile(`: \(([A-Za-z_$][A-Za-z0-9_$.]*(<[^()\n;{}]*>)?)\)(\s*=>)`)
|
|
352
|
-
normalizeParenNullishPattern = regexp.MustCompile(`\| \((null|undefined)\)`)
|
|
353
|
-
)
|
|
354
|
-
|
|
355
|
-
func normalizeParenthesizedTypeAnnotations(text string) string {
|
|
356
|
-
text = normalizeParenArrowTypePattern.ReplaceAllString(text, ": $1$3")
|
|
357
|
-
text = normalizeParenNullishPattern.ReplaceAllString(text, "| $1")
|
|
358
|
-
return text
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
func sourceFileKey(cwd string, file string) string {
|
|
362
|
-
rel, err := filepath.Rel(cwd, filepath.FromSlash(file))
|
|
363
|
-
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
|
|
364
|
-
return filepath.ToSlash(file)
|
|
365
|
-
}
|
|
366
|
-
return filepath.ToSlash(rel)
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
func newTransformCompilerDiagnostic(
|
|
370
|
-
file string,
|
|
371
|
-
code string,
|
|
372
|
-
message string,
|
|
373
|
-
) transformCompilerDiagnostic {
|
|
374
|
-
var ptr *string
|
|
375
|
-
if file != "" {
|
|
376
|
-
normalized := filepath.ToSlash(file)
|
|
377
|
-
ptr = &normalized
|
|
378
|
-
}
|
|
379
|
-
return transformCompilerDiagnostic{
|
|
380
|
-
File: ptr,
|
|
381
|
-
Category: "error",
|
|
382
|
-
Code: code,
|
|
383
|
-
MessageText: message,
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
func transformDiagnosticToCompilerDiagnostic(
|
|
388
|
-
diag Diagnostic,
|
|
389
|
-
) transformCompilerDiagnostic {
|
|
390
|
-
var ptr *string
|
|
391
|
-
if diag.File != "" {
|
|
392
|
-
normalized := filepath.ToSlash(diag.File)
|
|
393
|
-
ptr = &normalized
|
|
394
|
-
}
|
|
395
|
-
return transformCompilerDiagnostic{
|
|
396
|
-
File: ptr,
|
|
397
|
-
Category: "error",
|
|
398
|
-
Code: diag.Code,
|
|
399
|
-
Line: diag.Line,
|
|
400
|
-
Character: diag.Column,
|
|
401
|
-
MessageText: diag.Message,
|
|
402
|
-
}
|
|
403
|
-
}
|
|
1
|
+
package transform
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"flag"
|
|
7
|
+
"fmt"
|
|
8
|
+
"os"
|
|
9
|
+
"path/filepath"
|
|
10
|
+
"regexp"
|
|
11
|
+
"sort"
|
|
12
|
+
"strings"
|
|
13
|
+
|
|
14
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
15
|
+
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
16
|
+
"github.com/samchon/nestia/packages/core/native/plugin"
|
|
17
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
type transformProjectOutput struct {
|
|
21
|
+
Diagnostics []transformCompilerDiagnostic `json:"diagnostics,omitempty"`
|
|
22
|
+
TypeScript map[string]string `json:"typescript"`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type transformCompilerDiagnostic struct {
|
|
26
|
+
File *string `json:"file"`
|
|
27
|
+
Category string `json:"category"`
|
|
28
|
+
Code string `json:"code"`
|
|
29
|
+
Line int `json:"line,omitempty"`
|
|
30
|
+
Character int `json:"character,omitempty"`
|
|
31
|
+
MessageText string `json:"messageText"`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
func runTransform(args []string) int {
|
|
35
|
+
fs := flag.NewFlagSet("transform", flag.ContinueOnError)
|
|
36
|
+
fs.SetOutput(stderr)
|
|
37
|
+
file := fs.String("file", "", "absolute or cwd-relative path of the .ts file to transform")
|
|
38
|
+
tsconfigPath := fs.String("tsconfig", "tsconfig.json", "tsconfig.json owning --file")
|
|
39
|
+
cwdOverride := fs.String("cwd", "", "override the working directory")
|
|
40
|
+
out := fs.String("out", "", "write output to PATH")
|
|
41
|
+
output := fs.String("output", "ts", "transform output kind: ts")
|
|
42
|
+
pluginsJSON := fs.String("plugins-json", "", "ordered ttsc plugin payload")
|
|
43
|
+
if err := fs.Parse(args); err != nil {
|
|
44
|
+
return 2
|
|
45
|
+
}
|
|
46
|
+
if *output != "ts" {
|
|
47
|
+
fmt.Fprintf(stderr, "ttsc-nestia transform: unknown --output value %q\n", *output)
|
|
48
|
+
return 2
|
|
49
|
+
}
|
|
50
|
+
plan, err := plugin.ParsePlan(*pluginsJSON)
|
|
51
|
+
if err != nil {
|
|
52
|
+
fmt.Fprintf(stderr, "ttsc-nestia transform: %v\n", err)
|
|
53
|
+
return 2
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
cwd, ok := resolveCWD("ttsc-nestia transform", *cwdOverride)
|
|
57
|
+
if !ok {
|
|
58
|
+
return 2
|
|
59
|
+
}
|
|
60
|
+
prog, diags, err := driver.LoadProgram(cwd, *tsconfigPath, driver.LoadProgramOptions{
|
|
61
|
+
ForceEmit: true,
|
|
62
|
+
})
|
|
63
|
+
if err != nil {
|
|
64
|
+
fmt.Fprintf(stderr, "ttsc-nestia transform: %v\n", err)
|
|
65
|
+
return 2
|
|
66
|
+
}
|
|
67
|
+
if len(diags) > 0 {
|
|
68
|
+
driver.WritePrettyDiagnostics(stderr, diags, cwd)
|
|
69
|
+
return 2
|
|
70
|
+
}
|
|
71
|
+
defer prog.Close()
|
|
72
|
+
|
|
73
|
+
// AST-integration source-to-source: typia's, core's, and any linked
|
|
74
|
+
// contributor's per-file node transformers run inside one shared EmitContext
|
|
75
|
+
// and the result SourceFile is printed back as TypeScript (no JS script
|
|
76
|
+
// transformers), mirroring typia's `transform` subcommand. Injected namespace
|
|
77
|
+
// imports stay as ES imports the caller can type-strip per file, so there is
|
|
78
|
+
// no text-splice RewriteSet.
|
|
79
|
+
transformDiags := []Diagnostic{}
|
|
80
|
+
addDiagnostic := func(diag Diagnostic) {
|
|
81
|
+
transformDiags = append(transformDiags, diag)
|
|
82
|
+
}
|
|
83
|
+
typiaTransform := nestiaTypiaNodeTransform(prog, readTypiaPluginOptions(cwd, *tsconfigPath), addDiagnostic)
|
|
84
|
+
coreTransform := nestiaCoreNodeTransform(prog, plan, addDiagnostic)
|
|
85
|
+
contributorTransforms, contributorDiags := collectContributorEmitTransforms(prog, plan)
|
|
86
|
+
if len(contributorDiags) > 0 {
|
|
87
|
+
WriteTypiaTransformDiagnostics(stderr, contributorDiags, cwd)
|
|
88
|
+
return 3
|
|
89
|
+
}
|
|
90
|
+
transforms := append([]driver.PluginTransform{typiaTransform, coreTransform}, contributorTransforms...)
|
|
91
|
+
|
|
92
|
+
if *file == "" {
|
|
93
|
+
if *out != "" {
|
|
94
|
+
fmt.Fprintln(stderr, "ttsc-nestia transform: --out requires --file")
|
|
95
|
+
return 2
|
|
96
|
+
}
|
|
97
|
+
return runTransformProject(prog, cwd, transforms, &transformDiags)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
absFile := *file
|
|
101
|
+
if !filepath.IsAbs(absFile) {
|
|
102
|
+
absFile = filepath.Join(cwd, absFile)
|
|
103
|
+
}
|
|
104
|
+
absFile = filepath.ToSlash(absFile)
|
|
105
|
+
target := prog.SourceFile(absFile)
|
|
106
|
+
if target == nil {
|
|
107
|
+
fmt.Fprintf(stderr, "ttsc-nestia transform: source file is not in program: %s\n", absFile)
|
|
108
|
+
return 2
|
|
109
|
+
}
|
|
110
|
+
text := transformFileToTypeScript(prog, transforms, target)
|
|
111
|
+
if len(transformDiags) > 0 {
|
|
112
|
+
WriteTypiaTransformDiagnostics(stderr, transformDiags, cwd)
|
|
113
|
+
return 3
|
|
114
|
+
}
|
|
115
|
+
return writeSingleOutput(text, *out)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
func runTransformProject(
|
|
119
|
+
prog *driver.Program,
|
|
120
|
+
cwd string,
|
|
121
|
+
transforms []driver.PluginTransform,
|
|
122
|
+
transformDiags *[]Diagnostic,
|
|
123
|
+
) int {
|
|
124
|
+
output := transformProjectOutput{
|
|
125
|
+
Diagnostics: []transformCompilerDiagnostic{},
|
|
126
|
+
TypeScript: map[string]string{},
|
|
127
|
+
}
|
|
128
|
+
for _, sf := range prog.SourceFiles() {
|
|
129
|
+
if sf.IsDeclarationFile {
|
|
130
|
+
continue
|
|
131
|
+
}
|
|
132
|
+
key := sourceFileKey(cwd, filepath.ToSlash(sf.FileName()))
|
|
133
|
+
if filepath.IsAbs(key) || key == ".." || strings.HasPrefix(key, "../") {
|
|
134
|
+
continue
|
|
135
|
+
}
|
|
136
|
+
output.TypeScript[key] = transformFileToTypeScript(prog, transforms, sf)
|
|
137
|
+
}
|
|
138
|
+
for _, diag := range *transformDiags {
|
|
139
|
+
output.Diagnostics = append(output.Diagnostics, transformDiagnosticToCompilerDiagnostic(diag))
|
|
140
|
+
}
|
|
141
|
+
if err := json.NewEncoder(stdout).Encode(output); err != nil {
|
|
142
|
+
fmt.Fprintf(stderr, "ttsc-nestia transform: encode output: %v\n", err)
|
|
143
|
+
return 3
|
|
144
|
+
}
|
|
145
|
+
if len(output.Diagnostics) > 0 {
|
|
146
|
+
return 3
|
|
147
|
+
}
|
|
148
|
+
return 0
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// transformFileToTypeScript runs nestia's node transformers on one source file
|
|
152
|
+
// in a fresh EmitContext and prints the result as TypeScript. It deliberately
|
|
153
|
+
// skips the JS script transformers (type-erase, module-transform): the caller
|
|
154
|
+
// wants TS, so the namespace imports the transformers inject stay as ES imports.
|
|
155
|
+
func transformFileToTypeScript(
|
|
156
|
+
prog *driver.Program,
|
|
157
|
+
transforms []driver.PluginTransform,
|
|
158
|
+
sf *shimast.SourceFile,
|
|
159
|
+
) string {
|
|
160
|
+
options := prog.TSProgram.Options()
|
|
161
|
+
ec := shimprinter.NewEmitContext()
|
|
162
|
+
result := sf
|
|
163
|
+
for _, t := range transforms {
|
|
164
|
+
if t == nil {
|
|
165
|
+
continue
|
|
166
|
+
}
|
|
167
|
+
if next := t(ec, result); next != nil {
|
|
168
|
+
result = next
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
shimast.SetParentInChildrenUnset(result.AsNode())
|
|
172
|
+
writer := shimprinter.NewTextWriter(options.NewLine.GetNewLineCharacter(), 0)
|
|
173
|
+
printer := shimprinter.NewPrinter(shimprinter.PrinterOptions{NewLine: options.NewLine}, shimprinter.PrintHandlers{}, ec)
|
|
174
|
+
printer.Write(result.AsNode(), result, writer, nil)
|
|
175
|
+
return writer.String()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
func writeSingleOutput(text, outPath string) int {
|
|
179
|
+
if outPath == "" {
|
|
180
|
+
if _, err := bytes.NewReader([]byte(text)).WriteTo(stdout); err != nil {
|
|
181
|
+
fmt.Fprintf(stderr, "ttsc-nestia transform: write stdout: %v\n", err)
|
|
182
|
+
return 3
|
|
183
|
+
}
|
|
184
|
+
return 0
|
|
185
|
+
}
|
|
186
|
+
if dir := filepath.Dir(outPath); dir != "" {
|
|
187
|
+
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
188
|
+
fmt.Fprintf(stderr, "ttsc-nestia transform: mkdir: %v\n", err)
|
|
189
|
+
return 3
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if err := os.WriteFile(outPath, []byte(text), 0o644); err != nil {
|
|
193
|
+
fmt.Fprintf(stderr, "ttsc-nestia transform: write %s: %v\n", outPath, err)
|
|
194
|
+
return 3
|
|
195
|
+
}
|
|
196
|
+
return 0
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
type SourceRewrite struct {
|
|
200
|
+
start int
|
|
201
|
+
end int
|
|
202
|
+
replacement string
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
func ApplySourceRewrites(source string, rewrites []SourceRewrite) (string, error) {
|
|
206
|
+
sort.SliceStable(rewrites, func(i, j int) bool {
|
|
207
|
+
return rewrites[i].start > rewrites[j].start
|
|
208
|
+
})
|
|
209
|
+
for i := 0; i < len(rewrites)-1; i++ {
|
|
210
|
+
if rewrites[i+1].end > rewrites[i].start {
|
|
211
|
+
return "", fmt.Errorf("overlapping rewrites: [%d,%d) vs [%d,%d)",
|
|
212
|
+
rewrites[i+1].start, rewrites[i+1].end, rewrites[i].start, rewrites[i].end)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
output := source
|
|
216
|
+
for _, rewrite := range rewrites {
|
|
217
|
+
if rewrite.start < 0 || rewrite.end < rewrite.start || rewrite.end > len(output) {
|
|
218
|
+
return "", fmt.Errorf("invalid rewrite range [%d,%d)", rewrite.start, rewrite.end)
|
|
219
|
+
}
|
|
220
|
+
output = output[:rewrite.start] + rewrite.replacement + output[rewrite.end:]
|
|
221
|
+
}
|
|
222
|
+
return output, nil
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
func SourceFileText(target any) (string, bool) {
|
|
226
|
+
type sourceText interface {
|
|
227
|
+
Text() string
|
|
228
|
+
}
|
|
229
|
+
file, ok := target.(sourceText)
|
|
230
|
+
if !ok {
|
|
231
|
+
return "", false
|
|
232
|
+
}
|
|
233
|
+
return file.Text(), true
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// These patterns run once per transformed file in cleanupTypeScriptTransformText
|
|
237
|
+
// / normalizeParenthesizedTypeAnnotations. Compiling them at package scope keeps
|
|
238
|
+
// the per-file cost to a match instead of a recompile (the SDK `transform` path
|
|
239
|
+
// runs this for every emitted source file).
|
|
240
|
+
var (
|
|
241
|
+
cleanupImportTypeBlockPattern = regexp.MustCompile(`(?m)^import type \{([^{}\n]+)\} from`)
|
|
242
|
+
cleanupImportTypeLinePattern = regexp.MustCompile(`^import type \{\s*([^{}\n]+?)\s*\} from`)
|
|
243
|
+
cleanupImportBlankLinePattern = regexp.MustCompile(`(?m)(^import [^\n]+;\n)\n+(const |let |var |export )`)
|
|
244
|
+
cleanupInputIsParenPattern = regexp.MustCompile(`input is \(([A-Za-z_$][A-Za-z0-9_$.]*)\)`)
|
|
245
|
+
cleanupCollapseBlankCallPattern = regexp.MustCompile(`\n\n([A-Za-z_$][A-Za-z0-9_$]*\([^;\n]*\);?)`)
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
func cleanupTypeScriptTransformText(text string) string {
|
|
249
|
+
text = cleanupTransformedText(text)
|
|
250
|
+
text = normalizeParenthesizedTypeAnnotations(text)
|
|
251
|
+
text = cleanupImportTypeBlockPattern.ReplaceAllStringFunc(text, func(line string) string {
|
|
252
|
+
return cleanupImportTypeLinePattern.ReplaceAllString(line, "import type { $1 } from")
|
|
253
|
+
})
|
|
254
|
+
text = cleanupImportBlankLinePattern.ReplaceAllString(text, "$1$2")
|
|
255
|
+
text = strings.ReplaceAll(text, "=(() =>", "= (() =>")
|
|
256
|
+
text = strings.ReplaceAll(text, ": (any) =>", ": any =>")
|
|
257
|
+
text = strings.ReplaceAll(text, ": (boolean) =>", ": boolean =>")
|
|
258
|
+
text = cleanupInputIsParenPattern.ReplaceAllString(text, "input is $1")
|
|
259
|
+
text = strings.ReplaceAll(text, "return (success ? ", "return success ? ")
|
|
260
|
+
text = strings.ReplaceAll(text, "}) as any;", "} as any;")
|
|
261
|
+
text = strings.ReplaceAll(text, "(() => {\n const ", "(() => { const ")
|
|
262
|
+
text = strings.ReplaceAll(text, "(() => {\n let ", "(() => { let ")
|
|
263
|
+
text = strings.ReplaceAll(text, "(() => {\n return ", "(() => { return ")
|
|
264
|
+
text = strings.ReplaceAll(text, ";\n const ", "; const ")
|
|
265
|
+
text = strings.ReplaceAll(text, ";\n let ", "; let ")
|
|
266
|
+
text = strings.ReplaceAll(text, ";\n return ", "; return ")
|
|
267
|
+
text = strings.ReplaceAll(text, "\n };\n})()", "\n}; })()")
|
|
268
|
+
text = strings.ReplaceAll(text, "\n });\n})()", "\n}); })()")
|
|
269
|
+
text = strings.ReplaceAll(text, "\n }); let ", "\n}); let ")
|
|
270
|
+
text = strings.ReplaceAll(text, ";\n})()", "; })()")
|
|
271
|
+
text = strings.ReplaceAll(text, "\n ", "\n ")
|
|
272
|
+
text = cleanupCollapseBlankCallPattern.ReplaceAllString(text, "\n$1")
|
|
273
|
+
trimmed := strings.TrimRight(text, " \t\r\n")
|
|
274
|
+
if strings.HasSuffix(trimmed, ")") && !strings.HasSuffix(trimmed, ";") {
|
|
275
|
+
return trimmed + ";\n"
|
|
276
|
+
}
|
|
277
|
+
if text != "" && !strings.HasSuffix(text, "\n") {
|
|
278
|
+
return text + "\n"
|
|
279
|
+
}
|
|
280
|
+
return text
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
var (
|
|
284
|
+
normalizeParenArrowTypePattern = regexp.MustCompile(`: \(([A-Za-z_$][A-Za-z0-9_$.]*(<[^()\n;{}]*>)?)\)(\s*=>)`)
|
|
285
|
+
normalizeParenNullishPattern = regexp.MustCompile(`\| \((null|undefined)\)`)
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
func normalizeParenthesizedTypeAnnotations(text string) string {
|
|
289
|
+
text = normalizeParenArrowTypePattern.ReplaceAllString(text, ": $1$3")
|
|
290
|
+
text = normalizeParenNullishPattern.ReplaceAllString(text, "| $1")
|
|
291
|
+
return text
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
func sourceFileKey(cwd string, file string) string {
|
|
295
|
+
rel, err := filepath.Rel(cwd, filepath.FromSlash(file))
|
|
296
|
+
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
|
|
297
|
+
return filepath.ToSlash(file)
|
|
298
|
+
}
|
|
299
|
+
return filepath.ToSlash(rel)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
func newTransformCompilerDiagnostic(
|
|
303
|
+
file string,
|
|
304
|
+
code string,
|
|
305
|
+
message string,
|
|
306
|
+
) transformCompilerDiagnostic {
|
|
307
|
+
var ptr *string
|
|
308
|
+
if file != "" {
|
|
309
|
+
normalized := filepath.ToSlash(file)
|
|
310
|
+
ptr = &normalized
|
|
311
|
+
}
|
|
312
|
+
return transformCompilerDiagnostic{
|
|
313
|
+
File: ptr,
|
|
314
|
+
Category: "error",
|
|
315
|
+
Code: code,
|
|
316
|
+
MessageText: message,
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
func transformDiagnosticToCompilerDiagnostic(
|
|
321
|
+
diag Diagnostic,
|
|
322
|
+
) transformCompilerDiagnostic {
|
|
323
|
+
var ptr *string
|
|
324
|
+
if diag.File != "" {
|
|
325
|
+
normalized := filepath.ToSlash(diag.File)
|
|
326
|
+
ptr = &normalized
|
|
327
|
+
}
|
|
328
|
+
return transformCompilerDiagnostic{
|
|
329
|
+
File: ptr,
|
|
330
|
+
Category: "error",
|
|
331
|
+
Code: diag.Code,
|
|
332
|
+
Line: diag.Line,
|
|
333
|
+
Character: diag.Column,
|
|
334
|
+
MessageText: diag.Message,
|
|
335
|
+
}
|
|
336
|
+
}
|