@nestia/core 11.2.1 → 12.0.0-dev.20260520.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/MIGRATION.md +169 -0
  2. package/lib/adaptors/WebSocketAdaptor.js +7 -3
  3. package/lib/adaptors/WebSocketAdaptor.js.map +1 -1
  4. package/lib/decorators/DynamicModule.js.map +1 -1
  5. package/lib/decorators/EncryptedBody.js.map +1 -1
  6. package/lib/decorators/EncryptedController.js.map +1 -1
  7. package/lib/decorators/EncryptedModule.js.map +1 -1
  8. package/lib/decorators/EncryptedRoute.js.map +1 -1
  9. package/lib/decorators/HumanRoute.js.map +1 -1
  10. package/lib/decorators/NoTransformConfigurationError.js +5 -2
  11. package/lib/decorators/NoTransformConfigurationError.js.map +1 -1
  12. package/lib/decorators/PlainBody.js.map +1 -1
  13. package/lib/decorators/SwaggerCustomizer.js.map +1 -1
  14. package/lib/decorators/SwaggerExample.js.map +1 -1
  15. package/lib/decorators/TypedBody.js.map +1 -1
  16. package/lib/decorators/TypedException.js.map +1 -1
  17. package/lib/decorators/TypedFormData.js.map +1 -1
  18. package/lib/decorators/TypedHeaders.js.map +1 -1
  19. package/lib/decorators/TypedParam.js +5 -2
  20. package/lib/decorators/TypedParam.js.map +1 -1
  21. package/lib/decorators/TypedQuery.js.map +1 -1
  22. package/lib/decorators/TypedRoute.js.map +1 -1
  23. package/lib/decorators/WebSocketRoute.js.map +1 -1
  24. package/lib/decorators/doNotThrowTransformError.js.map +1 -1
  25. package/lib/decorators/internal/get_path_and_querify.js +5 -2
  26. package/lib/decorators/internal/get_path_and_querify.js.map +1 -1
  27. package/lib/decorators/internal/get_path_and_stringify.js +5 -2
  28. package/lib/decorators/internal/get_path_and_stringify.js.map +1 -1
  29. package/lib/decorators/internal/get_text_body.js.map +1 -1
  30. package/lib/decorators/internal/headers_to_object.js.map +1 -1
  31. package/lib/decorators/internal/is_request_body_undefined.js.map +1 -1
  32. package/lib/decorators/internal/load_controller.js +48 -16
  33. package/lib/decorators/internal/load_controller.js.map +1 -1
  34. package/lib/decorators/internal/route_error.js +3 -3
  35. package/lib/decorators/internal/route_error.js.map +1 -1
  36. package/lib/decorators/internal/validate_request_body.js +5 -2
  37. package/lib/decorators/internal/validate_request_body.js.map +1 -1
  38. package/lib/decorators/internal/validate_request_form_data.js +5 -2
  39. package/lib/decorators/internal/validate_request_form_data.js.map +1 -1
  40. package/lib/decorators/internal/validate_request_headers.js +5 -2
  41. package/lib/decorators/internal/validate_request_headers.js.map +1 -1
  42. package/lib/decorators/internal/validate_request_query.js +30 -4
  43. package/lib/decorators/internal/validate_request_query.js.map +1 -1
  44. package/lib/index.js.map +1 -1
  45. package/lib/transform.d.ts +2 -5
  46. package/lib/transform.js +48 -28
  47. package/lib/transform.js.map +1 -1
  48. package/lib/utils/ArrayUtil.js.map +1 -1
  49. package/lib/utils/ExceptionManager.js.map +1 -1
  50. package/lib/utils/Singleton.js.map +1 -1
  51. package/lib/utils/SourceFinder.js.map +1 -1
  52. package/lib/utils/VersioningStrategy.js.map +1 -1
  53. package/native/cmd/ttsc-nestia/build.go +434 -0
  54. package/native/cmd/ttsc-nestia/cleanup.go +408 -0
  55. package/native/cmd/ttsc-nestia/core_querify.go +227 -0
  56. package/native/cmd/ttsc-nestia/core_transform.go +1713 -0
  57. package/native/cmd/ttsc-nestia/core_websocket.go +115 -0
  58. package/native/cmd/ttsc-nestia/main.go +72 -0
  59. package/native/cmd/ttsc-nestia/path_rewrite.go +285 -0
  60. package/native/cmd/ttsc-nestia/printer.go +244 -0
  61. package/native/cmd/ttsc-nestia/rewrite.go +662 -0
  62. package/native/cmd/ttsc-nestia/sdk_metadata_json.go +327 -0
  63. package/native/cmd/ttsc-nestia/sdk_transform.go +1541 -0
  64. package/native/cmd/ttsc-nestia/transform.go +387 -0
  65. package/native/cmd/ttsc-nestia/typia_fast.go +326 -0
  66. package/native/cmd/ttsc-nestia/typia_replacement.go +24 -0
  67. package/native/go.mod +32 -0
  68. package/native/go.sum +54 -0
  69. package/native/plugin/plan.go +102 -0
  70. package/native/transform.cjs +21 -0
  71. package/package.json +27 -22
  72. package/src/decorators/NoTransformConfigurationError.ts +5 -2
  73. package/src/decorators/internal/load_controller.ts +50 -19
  74. package/src/decorators/internal/validate_request_query.ts +21 -2
  75. package/src/transform.ts +82 -35
  76. package/lib/decorators/internal/NoTransformConfigureError.d.ts +0 -1
  77. package/lib/decorators/internal/NoTransformConfigureError.js +0 -7
  78. package/lib/decorators/internal/NoTransformConfigureError.js.map +0 -1
  79. package/lib/options/INestiaTransformOptions.d.ts +0 -13
  80. package/lib/options/INestiaTransformOptions.js +0 -3
  81. package/lib/options/INestiaTransformOptions.js.map +0 -1
  82. package/lib/options/INestiaTransformProject.d.ts +0 -5
  83. package/lib/options/INestiaTransformProject.js +0 -3
  84. package/lib/options/INestiaTransformProject.js.map +0 -1
  85. package/lib/programmers/PlainBodyProgrammer.d.ts +0 -9
  86. package/lib/programmers/PlainBodyProgrammer.js +0 -58
  87. package/lib/programmers/PlainBodyProgrammer.js.map +0 -1
  88. package/lib/programmers/TypedBodyProgrammer.d.ts +0 -9
  89. package/lib/programmers/TypedBodyProgrammer.js +0 -94
  90. package/lib/programmers/TypedBodyProgrammer.js.map +0 -1
  91. package/lib/programmers/TypedFormDataBodyProgrammer.d.ts +0 -9
  92. package/lib/programmers/TypedFormDataBodyProgrammer.js +0 -65
  93. package/lib/programmers/TypedFormDataBodyProgrammer.js.map +0 -1
  94. package/lib/programmers/TypedHeadersProgrammer.d.ts +0 -9
  95. package/lib/programmers/TypedHeadersProgrammer.js +0 -38
  96. package/lib/programmers/TypedHeadersProgrammer.js.map +0 -1
  97. package/lib/programmers/TypedParamProgrammer.d.ts +0 -10
  98. package/lib/programmers/TypedParamProgrammer.js +0 -32
  99. package/lib/programmers/TypedParamProgrammer.js.map +0 -1
  100. package/lib/programmers/TypedQueryBodyProgrammer.d.ts +0 -9
  101. package/lib/programmers/TypedQueryBodyProgrammer.js +0 -74
  102. package/lib/programmers/TypedQueryBodyProgrammer.js.map +0 -1
  103. package/lib/programmers/TypedQueryProgrammer.d.ts +0 -9
  104. package/lib/programmers/TypedQueryProgrammer.js +0 -75
  105. package/lib/programmers/TypedQueryProgrammer.js.map +0 -1
  106. package/lib/programmers/TypedQueryRouteProgrammer.d.ts +0 -9
  107. package/lib/programmers/TypedQueryRouteProgrammer.js +0 -75
  108. package/lib/programmers/TypedQueryRouteProgrammer.js.map +0 -1
  109. package/lib/programmers/TypedRouteProgrammer.d.ts +0 -9
  110. package/lib/programmers/TypedRouteProgrammer.js +0 -79
  111. package/lib/programmers/TypedRouteProgrammer.js.map +0 -1
  112. package/lib/programmers/http/HttpAssertQuerifyProgrammer.d.ts +0 -9
  113. package/lib/programmers/http/HttpAssertQuerifyProgrammer.js +0 -39
  114. package/lib/programmers/http/HttpAssertQuerifyProgrammer.js.map +0 -1
  115. package/lib/programmers/http/HttpIsQuerifyProgrammer.d.ts +0 -9
  116. package/lib/programmers/http/HttpIsQuerifyProgrammer.js +0 -36
  117. package/lib/programmers/http/HttpIsQuerifyProgrammer.js.map +0 -1
  118. package/lib/programmers/http/HttpQuerifyProgrammer.d.ts +0 -9
  119. package/lib/programmers/http/HttpQuerifyProgrammer.js +0 -50
  120. package/lib/programmers/http/HttpQuerifyProgrammer.js.map +0 -1
  121. package/lib/programmers/http/HttpValidateQuerifyProgrammer.d.ts +0 -9
  122. package/lib/programmers/http/HttpValidateQuerifyProgrammer.js +0 -40
  123. package/lib/programmers/http/HttpValidateQuerifyProgrammer.js.map +0 -1
  124. package/lib/programmers/internal/CoreMetadataUtil.d.ts +0 -5
  125. package/lib/programmers/internal/CoreMetadataUtil.js +0 -19
  126. package/lib/programmers/internal/CoreMetadataUtil.js.map +0 -1
  127. package/lib/transformers/FileTransformer.d.ts +0 -5
  128. package/lib/transformers/FileTransformer.js +0 -80
  129. package/lib/transformers/FileTransformer.js.map +0 -1
  130. package/lib/transformers/MethodTransformer.d.ts +0 -8
  131. package/lib/transformers/MethodTransformer.js +0 -58
  132. package/lib/transformers/MethodTransformer.js.map +0 -1
  133. package/lib/transformers/NodeTransformer.d.ts +0 -8
  134. package/lib/transformers/NodeTransformer.js +0 -24
  135. package/lib/transformers/NodeTransformer.js.map +0 -1
  136. package/lib/transformers/ParameterDecoratorTransformer.d.ts +0 -9
  137. package/lib/transformers/ParameterDecoratorTransformer.js +0 -104
  138. package/lib/transformers/ParameterDecoratorTransformer.js.map +0 -1
  139. package/lib/transformers/ParameterTransformer.d.ts +0 -8
  140. package/lib/transformers/ParameterTransformer.js +0 -37
  141. package/lib/transformers/ParameterTransformer.js.map +0 -1
  142. package/lib/transformers/TypedRouteTransformer.d.ts +0 -9
  143. package/lib/transformers/TypedRouteTransformer.js +0 -68
  144. package/lib/transformers/TypedRouteTransformer.js.map +0 -1
  145. package/lib/transformers/WebSocketRouteTransformer.d.ts +0 -9
  146. package/lib/transformers/WebSocketRouteTransformer.js +0 -72
  147. package/lib/transformers/WebSocketRouteTransformer.js.map +0 -1
  148. package/src/decorators/internal/NoTransformConfigureError.ts +0 -2
  149. package/src/options/INestiaTransformOptions.ts +0 -34
  150. package/src/options/INestiaTransformProject.ts +0 -10
  151. package/src/programmers/PlainBodyProgrammer.ts +0 -72
  152. package/src/programmers/TypedBodyProgrammer.ts +0 -148
  153. package/src/programmers/TypedFormDataBodyProgrammer.ts +0 -118
  154. package/src/programmers/TypedHeadersProgrammer.ts +0 -65
  155. package/src/programmers/TypedParamProgrammer.ts +0 -33
  156. package/src/programmers/TypedQueryBodyProgrammer.ts +0 -113
  157. package/src/programmers/TypedQueryProgrammer.ts +0 -115
  158. package/src/programmers/TypedQueryRouteProgrammer.ts +0 -107
  159. package/src/programmers/TypedRouteProgrammer.ts +0 -103
  160. package/src/programmers/http/HttpAssertQuerifyProgrammer.ts +0 -74
  161. package/src/programmers/http/HttpIsQuerifyProgrammer.ts +0 -77
  162. package/src/programmers/http/HttpQuerifyProgrammer.ts +0 -110
  163. package/src/programmers/http/HttpValidateQuerifyProgrammer.ts +0 -78
  164. package/src/programmers/internal/CoreMetadataUtil.ts +0 -21
  165. package/src/transformers/FileTransformer.ts +0 -109
  166. package/src/transformers/MethodTransformer.ts +0 -103
  167. package/src/transformers/NodeTransformer.ts +0 -23
  168. package/src/transformers/ParameterDecoratorTransformer.ts +0 -143
  169. package/src/transformers/ParameterTransformer.ts +0 -57
  170. package/src/transformers/TypedRouteTransformer.ts +0 -85
  171. package/src/transformers/WebSocketRouteTransformer.ts +0 -120
@@ -0,0 +1,115 @@
1
+ package main
2
+
3
+ import (
4
+ "fmt"
5
+ "strings"
6
+
7
+ shimast "github.com/microsoft/typescript-go/shim/ast"
8
+ shimscanner "github.com/microsoft/typescript-go/shim/scanner"
9
+ "github.com/samchon/ttsc/packages/ttsc/driver"
10
+ )
11
+
12
+ func validateNestiaCoreWebSocketRoute(
13
+ prog *driver.Program,
14
+ context nestiaCoreFileContext,
15
+ method *shimast.Node,
16
+ call *shimast.CallExpression,
17
+ segments []string,
18
+ ) []typiaTransformDiagnostic {
19
+ if call == nil || len(segments) == 0 || segments[len(segments)-1] != "WebSocketRoute" {
20
+ _ = call
21
+ return nil
22
+ }
23
+ diagnostics := []typiaTransformDiagnostic{}
24
+ accepted := false
25
+ methodDecl := method.AsMethodDeclaration()
26
+ if methodDecl == nil || methodDecl.Parameters == nil {
27
+ return []typiaTransformDiagnostic{nestiaCoreWebSocketDiagnostic(context.file, method, "WebSocketRoute", fmt.Sprintf(
28
+ "method %q must have at least one parameter decorated by @WebSocketRoute.Acceptor().",
29
+ nestiaSDKMethodName(method),
30
+ ))}
31
+ }
32
+ for _, param := range methodDecl.Parameters.Nodes {
33
+ category := nestiaCoreWebSocketParameterCategory(prog, param)
34
+ name := nestiaSDKParameterName(param)
35
+ if category == "" {
36
+ diagnostics = append(diagnostics, nestiaCoreWebSocketDiagnostic(context.file, param, "WebSocketRoute", fmt.Sprintf(
37
+ "parameter %q is not decorated with nested function of WebSocketRoute module.",
38
+ name,
39
+ )))
40
+ continue
41
+ }
42
+ switch category {
43
+ case "Acceptor":
44
+ accepted = true
45
+ if strings.HasPrefix(nestiaCoreWebSocketParameterTypeName(param), "WebSocketAcceptor") == false {
46
+ diagnostics = append(diagnostics, nestiaCoreWebSocketDiagnostic(context.file, param, "WebSocketRoute", fmt.Sprintf(
47
+ "parameter %q must have WebSocketAcceptor<Header, Provider, Listener> type.",
48
+ name,
49
+ )))
50
+ }
51
+ case "Driver":
52
+ if strings.HasPrefix(nestiaCoreWebSocketParameterTypeName(param), "Driver") == false {
53
+ diagnostics = append(diagnostics, nestiaCoreWebSocketDiagnostic(context.file, param, "WebSocketRoute", fmt.Sprintf(
54
+ "parameter %q must have Driver<Listener> type.",
55
+ name,
56
+ )))
57
+ }
58
+ }
59
+ }
60
+ if accepted == false {
61
+ diagnostics = append(diagnostics, nestiaCoreWebSocketDiagnostic(context.file, method, "WebSocketRoute", fmt.Sprintf(
62
+ "method %q must have at least one parameter decorated by @WebSocketRoute.Acceptor().",
63
+ nestiaSDKMethodName(method),
64
+ )))
65
+ }
66
+ return diagnostics
67
+ }
68
+
69
+ func nestiaCoreWebSocketParameterCategory(prog *driver.Program, param *shimast.Node) string {
70
+ if param == nil || len(param.Decorators()) != 1 {
71
+ return ""
72
+ }
73
+ call, segments, ok := nestiaCoreDecoratorCall(prog, param.Decorators()[0])
74
+ if ok == false || len(segments) < 2 || segments[len(segments)-2] != "WebSocketRoute" {
75
+ _ = call
76
+ return ""
77
+ }
78
+ return segments[len(segments)-1]
79
+ }
80
+
81
+ func nestiaCoreWebSocketParameterTypeName(param *shimast.Node) string {
82
+ if param == nil || param.AsParameterDeclaration() == nil || param.AsParameterDeclaration().Type == nil {
83
+ return ""
84
+ }
85
+ text := nestiaSDKTypeNodeText(param.AsParameterDeclaration().Type)
86
+ text = strings.TrimSpace(text)
87
+ if index := strings.Index(text, "<"); index >= 0 {
88
+ text = text[:index]
89
+ }
90
+ if index := strings.LastIndex(text, "."); index >= 0 {
91
+ text = text[index+1:]
92
+ }
93
+ return text
94
+ }
95
+
96
+ func nestiaCoreWebSocketDiagnostic(file *shimast.SourceFile, node *shimast.Node, kind string, message string) typiaTransformDiagnostic {
97
+ filePath := ""
98
+ line, column := 0, 0
99
+ if file != nil {
100
+ filePath = file.FileName()
101
+ if node != nil {
102
+ if pos := node.Pos(); pos >= 0 {
103
+ l, c := shimscanner.GetECMALineAndByteOffsetOfPosition(file, pos)
104
+ line, column = l+1, c+1
105
+ }
106
+ }
107
+ }
108
+ return typiaTransformDiagnostic{
109
+ File: filePath,
110
+ Line: line,
111
+ Column: column,
112
+ Code: "nestia.core." + kind,
113
+ Message: message,
114
+ }
115
+ }
@@ -0,0 +1,72 @@
1
+ package main
2
+
3
+ import (
4
+ "fmt"
5
+ "io"
6
+ "os"
7
+ "runtime/debug"
8
+ )
9
+
10
+ var (
11
+ stdout io.Writer = os.Stdout
12
+ stderr io.Writer = os.Stderr
13
+ )
14
+
15
+ func main() {
16
+ os.Exit(runSafe(os.Args[1:]))
17
+ }
18
+
19
+ // runSafe wraps run() in a panic recovery envelope so that any unexpected
20
+ // panic surfaces as a one-line transform-diagnostic on stderr instead of
21
+ // a multi-line raw Go stack trace. The diagnostic uses the same `<file> -
22
+ // error TS(code): message` shape as every other nestia / typia diagnostic;
23
+ // ttsc reads it via `error.stderr` (`RuntimeCompiler.compile` at
24
+ // `ConfigAnalyzer.ts:170-174`) rather than its structured-diagnostic regex,
25
+ // which is a pre-existing protocol shared by all `nestia.*` codes. The
26
+ // full stack is preserved behind NESTIA_NATIVE_DEBUG_STACK for triage.
27
+ func runSafe(args []string) (code int) {
28
+ defer func() {
29
+ if exp := recover(); exp != nil {
30
+ diag := typiaTransformDiagnostic{
31
+ Code: "nestia.internal.panic",
32
+ Message: fmt.Sprintf("ttsc-nestia panicked: %v", exp),
33
+ }
34
+ writeTypiaTransformDiagnostics(stderr, []typiaTransformDiagnostic{diag}, "")
35
+ if os.Getenv("NESTIA_NATIVE_DEBUG_STACK") != "" {
36
+ fmt.Fprintln(stderr, string(debug.Stack()))
37
+ }
38
+ code = 3
39
+ }
40
+ }()
41
+ return run(args)
42
+ }
43
+
44
+ func run(args []string) int {
45
+ if len(args) == 0 {
46
+ return runHelp(nil)
47
+ }
48
+ command := args[0]
49
+ rest := args[1:]
50
+ switch command {
51
+ case "build":
52
+ return runBuild(rest)
53
+ case "check":
54
+ return runCheck(rest)
55
+ case "transform":
56
+ return runTransform(rest)
57
+ case "version":
58
+ fmt.Fprintln(stdout, "ttsc-nestia 0.1.0")
59
+ return 0
60
+ case "help", "-h", "--help":
61
+ return runHelp(rest)
62
+ default:
63
+ fmt.Fprintf(stderr, "ttsc-nestia: unknown command %q\n", command)
64
+ return 2
65
+ }
66
+ }
67
+
68
+ func runHelp(args []string) int {
69
+ _ = args
70
+ fmt.Fprintln(stdout, "usage: ttsc-nestia <build|check|transform|version>")
71
+ return 0
72
+ }
@@ -0,0 +1,285 @@
1
+ package main
2
+
3
+ import (
4
+ "path/filepath"
5
+ "sort"
6
+ "strings"
7
+
8
+ shimast "github.com/microsoft/typescript-go/shim/ast"
9
+ shimcore "github.com/microsoft/typescript-go/shim/core"
10
+ "github.com/samchon/ttsc/packages/ttsc/driver"
11
+ )
12
+
13
+ type pathsRewriter struct {
14
+ basePath string
15
+ outDir string
16
+ patterns []pathsPattern
17
+ rootDir string
18
+ sourceFiles map[string]string
19
+ }
20
+
21
+ type pathsPattern struct {
22
+ pattern string
23
+ targets []string
24
+ }
25
+
26
+ func newPathsRewriter(prog *driver.Program) *pathsRewriter {
27
+ out := &pathsRewriter{sourceFiles: map[string]string{}}
28
+ if prog == nil || prog.ParsedConfig == nil || prog.ParsedConfig.ParsedConfig == nil || prog.ParsedConfig.ParsedConfig.CompilerOptions == nil {
29
+ return out
30
+ }
31
+ options := prog.ParsedConfig.ParsedConfig.CompilerOptions
32
+ out.basePath = filepath.Clean(options.GetPathsBasePath(prog.Host.GetCurrentDirectory()))
33
+ out.outDir = optionalPath(options.OutDir, prog.Host.GetCurrentDirectory())
34
+ out.rootDir = optionalPath(options.RootDir, prog.Host.GetCurrentDirectory())
35
+ files := prog.SourceFiles()
36
+ if out.rootDir == "" {
37
+ out.rootDir = commonSourceDir(files)
38
+ }
39
+ for _, file := range files {
40
+ name := normalizePath(file.FileName())
41
+ out.sourceFiles[name] = name
42
+ out.sourceFiles[stripKnownSourceExtension(name)] = name
43
+ }
44
+ if options.Paths != nil {
45
+ for pattern, targets := range options.Paths.Entries() {
46
+ out.patterns = append(out.patterns, pathsPattern{
47
+ pattern: pattern,
48
+ targets: append([]string(nil), targets...),
49
+ })
50
+ }
51
+ }
52
+ sort.SliceStable(out.patterns, func(i, j int) bool {
53
+ return patternRank(out.patterns[i].pattern) > patternRank(out.patterns[j].pattern)
54
+ })
55
+ return out
56
+ }
57
+
58
+ func (r *pathsRewriter) applyAll(files []*shimast.SourceFile) {
59
+ if r == nil || len(r.patterns) == 0 {
60
+ return
61
+ }
62
+ for _, file := range files {
63
+ r.apply(file)
64
+ }
65
+ }
66
+
67
+ func (r *pathsRewriter) apply(file *shimast.SourceFile) {
68
+ if r == nil || file == nil || len(r.patterns) == 0 {
69
+ return
70
+ }
71
+ visitModuleSpecifiers(file.AsNode(), func(lit *shimast.Node) {
72
+ if lit == nil || lit.Kind != shimast.KindStringLiteral {
73
+ return
74
+ }
75
+ spec := lit.Text()
76
+ rewritten, ok := r.rewrite(file.FileName(), spec)
77
+ if ok && rewritten != spec {
78
+ lit.AsStringLiteral().Text = rewritten
79
+ lit.Flags |= shimast.NodeFlagsSynthesized
80
+ lit.Loc = shimcore.UndefinedTextRange()
81
+ }
82
+ })
83
+ }
84
+
85
+ func visitModuleSpecifiers(node *shimast.Node, visit func(*shimast.Node)) {
86
+ if node == nil {
87
+ return
88
+ }
89
+ switch node.Kind {
90
+ case shimast.KindImportDeclaration:
91
+ visit(node.AsImportDeclaration().ModuleSpecifier)
92
+ case shimast.KindExportDeclaration:
93
+ visit(node.AsExportDeclaration().ModuleSpecifier)
94
+ case shimast.KindImportEqualsDeclaration:
95
+ ref := node.AsImportEqualsDeclaration().ModuleReference
96
+ if ref != nil && ref.Kind == shimast.KindExternalModuleReference {
97
+ visit(ref.AsExternalModuleReference().Expression)
98
+ }
99
+ case shimast.KindImportType:
100
+ arg := node.AsImportTypeNode().Argument
101
+ if arg != nil && arg.Kind == shimast.KindLiteralType {
102
+ visit(arg.AsLiteralTypeNode().Literal)
103
+ }
104
+ case shimast.KindModuleDeclaration:
105
+ decl := node.AsModuleDeclaration()
106
+ if decl != nil {
107
+ visit(decl.Name())
108
+ }
109
+ case shimast.KindCallExpression:
110
+ call := node.AsCallExpression()
111
+ if isModuleSpecifierCall(call) && call.Arguments != nil && len(call.Arguments.Nodes) > 0 {
112
+ visit(call.Arguments.Nodes[0])
113
+ }
114
+ }
115
+ node.ForEachChild(func(child *shimast.Node) bool {
116
+ visitModuleSpecifiers(child, visit)
117
+ return false
118
+ })
119
+ }
120
+
121
+ func isModuleSpecifierCall(call *shimast.CallExpression) bool {
122
+ if call == nil || call.Expression == nil {
123
+ return false
124
+ }
125
+ switch call.Expression.Kind {
126
+ case shimast.KindImportKeyword:
127
+ return true
128
+ case shimast.KindIdentifier:
129
+ return call.Expression.Text() == "require"
130
+ default:
131
+ return false
132
+ }
133
+ }
134
+
135
+ func (r *pathsRewriter) rewrite(fromSource string, specifier string) (string, bool) {
136
+ if specifier == "" || strings.HasPrefix(specifier, ".") || strings.HasPrefix(specifier, "/") {
137
+ return specifier, false
138
+ }
139
+ targetSource, ok := r.resolveSource(specifier)
140
+ if !ok {
141
+ return specifier, false
142
+ }
143
+ fromOut := r.outputPathForSource(fromSource)
144
+ targetOut := r.outputPathForSource(targetSource)
145
+ if fromOut == "" || targetOut == "" {
146
+ return specifier, false
147
+ }
148
+ rel, err := filepath.Rel(filepath.Dir(fromOut), targetOut)
149
+ if err != nil {
150
+ return specifier, false
151
+ }
152
+ rel = filepath.ToSlash(rel)
153
+ if !strings.HasPrefix(rel, ".") {
154
+ rel = "./" + rel
155
+ }
156
+ return rel, true
157
+ }
158
+
159
+ func (r *pathsRewriter) resolveSource(specifier string) (string, bool) {
160
+ for _, pattern := range r.patterns {
161
+ star, ok := matchPattern(pattern.pattern, specifier)
162
+ if !ok {
163
+ continue
164
+ }
165
+ for _, target := range pattern.targets {
166
+ candidate := strings.Replace(target, "*", star, 1)
167
+ resolved := normalizePath(filepath.Join(r.basePath, candidate))
168
+ if source, ok := r.lookupSource(resolved); ok {
169
+ return source, true
170
+ }
171
+ }
172
+ }
173
+ return "", false
174
+ }
175
+
176
+ func (r *pathsRewriter) lookupSource(candidate string) (string, bool) {
177
+ if source, ok := r.sourceFiles[normalizePath(candidate)]; ok {
178
+ return source, true
179
+ }
180
+ stem := stripKnownSourceExtension(normalizePath(candidate))
181
+ if source, ok := r.sourceFiles[stem]; ok {
182
+ return source, true
183
+ }
184
+ for _, ext := range []string{".ts", ".tsx", ".mts", ".cts"} {
185
+ if source, ok := r.sourceFiles[stem+ext]; ok {
186
+ return source, true
187
+ }
188
+ }
189
+ for _, ext := range []string{".ts", ".tsx", ".mts", ".cts"} {
190
+ if source, ok := r.sourceFiles[normalizePath(filepath.Join(stem, "index"+ext))]; ok {
191
+ return source, true
192
+ }
193
+ }
194
+ return "", false
195
+ }
196
+
197
+ func (r *pathsRewriter) outputPathForSource(source string) string {
198
+ if r.outDir == "" || r.rootDir == "" {
199
+ return ""
200
+ }
201
+ rel, err := filepath.Rel(r.rootDir, source)
202
+ if err != nil || isOutsideRelativePath(rel) {
203
+ return ""
204
+ }
205
+ return normalizePath(filepath.Join(r.outDir, replaceSourceExtension(rel, emittedJavaScriptExtension(rel))))
206
+ }
207
+
208
+ func emittedJavaScriptExtension(source string) string {
209
+ switch strings.ToLower(filepath.Ext(source)) {
210
+ case ".mts":
211
+ return ".mjs"
212
+ case ".cts":
213
+ return ".cjs"
214
+ default:
215
+ return ".js"
216
+ }
217
+ }
218
+
219
+ func matchPattern(pattern string, specifier string) (string, bool) {
220
+ if !strings.Contains(pattern, "*") {
221
+ return "", pattern == specifier
222
+ }
223
+ parts := strings.SplitN(pattern, "*", 2)
224
+ if !strings.HasPrefix(specifier, parts[0]) || !strings.HasSuffix(specifier, parts[1]) {
225
+ return "", false
226
+ }
227
+ return specifier[len(parts[0]) : len(specifier)-len(parts[1])], true
228
+ }
229
+
230
+ func patternRank(pattern string) int {
231
+ return len(strings.ReplaceAll(pattern, "*", ""))
232
+ }
233
+
234
+ func optionalPath(value string, cwd string) string {
235
+ if value == "" {
236
+ return ""
237
+ }
238
+ if filepath.IsAbs(value) {
239
+ return normalizePath(value)
240
+ }
241
+ return normalizePath(filepath.Join(cwd, value))
242
+ }
243
+
244
+ func commonSourceDir(files []*shimast.SourceFile) string {
245
+ if len(files) == 0 {
246
+ return ""
247
+ }
248
+ common := normalizePath(filepath.Dir(files[0].FileName()))
249
+ for _, file := range files[1:] {
250
+ dir := normalizePath(filepath.Dir(file.FileName()))
251
+ for common != "" && !strings.HasPrefix(dir+"/", common+"/") {
252
+ next := filepath.Dir(common)
253
+ if next == common {
254
+ return common
255
+ }
256
+ common = normalizePath(next)
257
+ }
258
+ }
259
+ return common
260
+ }
261
+
262
+ func normalizePath(value string) string {
263
+ if value == "" {
264
+ return ""
265
+ }
266
+ return filepath.ToSlash(filepath.Clean(value))
267
+ }
268
+
269
+ func isOutsideRelativePath(rel string) bool {
270
+ return rel == ".." || strings.HasPrefix(filepath.ToSlash(rel), "../")
271
+ }
272
+
273
+ func stripKnownSourceExtension(value string) string {
274
+ lower := strings.ToLower(value)
275
+ for _, ext := range []string{".d.ts", ".d.mts", ".d.cts", ".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"} {
276
+ if strings.HasSuffix(lower, ext) {
277
+ return value[:len(value)-len(ext)]
278
+ }
279
+ }
280
+ return strings.TrimSuffix(value, filepath.Ext(value))
281
+ }
282
+
283
+ func replaceSourceExtension(value string, ext string) string {
284
+ return stripKnownSourceExtension(filepath.ToSlash(value)) + ext
285
+ }
@@ -0,0 +1,244 @@
1
+ package main
2
+
3
+ import (
4
+ shimast "github.com/microsoft/typescript-go/shim/ast"
5
+ shimprinter "github.com/microsoft/typescript-go/shim/printer"
6
+ )
7
+
8
+ const nestiaKindColonToken = shimast.KindQuestionToken + 1
9
+
10
+ func emitNestiaWithIdentifierSubstitutions(
11
+ node *shimast.Node,
12
+ sourceFile *shimast.SourceFile,
13
+ substitutions map[string]string,
14
+ ) string {
15
+ node = stripNestiaTypeSyntax(node)
16
+ node = rewriteNestiaIdentifiers(node, substitutions)
17
+ normalizeNestiaSyntheticTokens(node)
18
+ return emitNestiaNode(node, sourceFile)
19
+ }
20
+
21
+ func emitNestiaPreservingTypesWithIdentifierSubstitutions(
22
+ node *shimast.Node,
23
+ sourceFile *shimast.SourceFile,
24
+ substitutions map[string]string,
25
+ ) string {
26
+ node = rewriteNestiaIdentifiers(node, substitutions)
27
+ normalizeNestiaSyntheticTokens(node)
28
+ return emitNestiaNode(node, sourceFile)
29
+ }
30
+
31
+ func emitNestiaNode(node *shimast.Node, sourceFile *shimast.SourceFile) string {
32
+ return shimprinter.NewPrinter(shimprinter.PrinterOptions{
33
+ RemoveComments: true,
34
+ NewLine: 2,
35
+ }, shimprinter.PrintHandlers{}, nil).Emit(node, sourceFile)
36
+ }
37
+
38
+ func stripNestiaTypeSyntax(node *shimast.Node) *shimast.Node {
39
+ if node == nil {
40
+ return nil
41
+ }
42
+ factory := shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
43
+ var visitor *shimast.NodeVisitor
44
+ visitor = shimast.NewNodeVisitor(func(current *shimast.Node) *shimast.Node {
45
+ if current == nil {
46
+ return nil
47
+ }
48
+ switch current.Kind {
49
+ case shimast.KindAsExpression, shimast.KindSatisfiesExpression, shimast.KindTypeAssertionExpression, shimast.KindNonNullExpression:
50
+ return visitor.VisitNode(current.Expression())
51
+ case shimast.KindVariableDeclaration:
52
+ decl := current.AsVariableDeclaration()
53
+ return factory.UpdateVariableDeclaration(
54
+ decl,
55
+ visitor.VisitNode(decl.Name()),
56
+ nil,
57
+ nil,
58
+ visitor.VisitNode(decl.Initializer),
59
+ )
60
+ case shimast.KindParameter:
61
+ parameter := current.AsParameterDeclaration()
62
+ return factory.UpdateParameterDeclaration(
63
+ parameter,
64
+ visitor.VisitModifiers(parameter.Modifiers()),
65
+ visitor.VisitNode(parameter.DotDotDotToken),
66
+ visitor.VisitNode(parameter.Name()),
67
+ nil,
68
+ nil,
69
+ visitor.VisitNode(parameter.Initializer),
70
+ )
71
+ case shimast.KindArrowFunction:
72
+ arrow := current.AsArrowFunction()
73
+ return factory.UpdateArrowFunction(
74
+ arrow,
75
+ visitor.VisitModifiers(arrow.Modifiers()),
76
+ nil,
77
+ visitor.VisitNodes(arrow.Parameters),
78
+ nil,
79
+ nil,
80
+ visitor.VisitNode(arrow.EqualsGreaterThanToken),
81
+ visitor.VisitNode(arrow.Body),
82
+ )
83
+ case shimast.KindCallExpression:
84
+ call := current.AsCallExpression()
85
+ return factory.UpdateCallExpression(
86
+ call,
87
+ visitor.VisitNode(call.Expression),
88
+ visitor.VisitNode(call.QuestionDotToken),
89
+ nil,
90
+ visitor.VisitNodes(call.Arguments),
91
+ call.Flags,
92
+ )
93
+ case shimast.KindNewExpression:
94
+ expr := current.AsNewExpression()
95
+ return factory.UpdateNewExpression(
96
+ expr,
97
+ visitor.VisitNode(expr.Expression),
98
+ nil,
99
+ visitor.VisitNodes(expr.Arguments),
100
+ )
101
+ default:
102
+ return visitor.VisitEachChild(current)
103
+ }
104
+ }, factory, shimast.NodeVisitorHooks{})
105
+ return visitor.VisitNode(node)
106
+ }
107
+
108
+ func rewriteNestiaIdentifiers(
109
+ node *shimast.Node,
110
+ substitutions map[string]string,
111
+ ) *shimast.Node {
112
+ if node == nil || len(substitutions) == 0 {
113
+ return node
114
+ }
115
+ factory := shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
116
+ var visitor *shimast.NodeVisitor
117
+ visitor = shimast.NewNodeVisitor(func(current *shimast.Node) *shimast.Node {
118
+ if current == nil {
119
+ return nil
120
+ }
121
+ switch current.Kind {
122
+ case shimast.KindIdentifier:
123
+ if replacement, ok := substitutions[current.Text()]; ok {
124
+ return rewriteNestiaIdentifierExpression(factory, replacement)
125
+ }
126
+ return current
127
+ case shimast.KindVariableDeclaration:
128
+ decl := current.AsVariableDeclaration()
129
+ return factory.UpdateVariableDeclaration(
130
+ decl,
131
+ decl.Name(),
132
+ nil,
133
+ nil,
134
+ visitor.VisitNode(decl.Initializer),
135
+ )
136
+ case shimast.KindParameter:
137
+ parameter := current.AsParameterDeclaration()
138
+ return factory.UpdateParameterDeclaration(
139
+ parameter,
140
+ parameter.Modifiers(),
141
+ parameter.DotDotDotToken,
142
+ parameter.Name(),
143
+ nil,
144
+ nil,
145
+ visitor.VisitNode(parameter.Initializer),
146
+ )
147
+ case shimast.KindPropertyAssignment:
148
+ assignment := current.AsPropertyAssignment()
149
+ return factory.UpdatePropertyAssignment(
150
+ assignment,
151
+ assignment.Modifiers(),
152
+ assignment.Name(),
153
+ assignment.PostfixToken,
154
+ nil,
155
+ visitor.VisitNode(assignment.Initializer),
156
+ )
157
+ case shimast.KindShorthandPropertyAssignment:
158
+ assignment := current.AsShorthandPropertyAssignment()
159
+ name := assignment.Name()
160
+ if name != nil && name.Kind == shimast.KindIdentifier {
161
+ if replacement, ok := substitutions[name.Text()]; ok &&
162
+ assignment.ObjectAssignmentInitializer == nil {
163
+ return factory.NewPropertyAssignment(
164
+ assignment.Modifiers(),
165
+ name,
166
+ assignment.PostfixToken,
167
+ nil,
168
+ rewriteNestiaIdentifierExpression(factory, replacement),
169
+ )
170
+ }
171
+ }
172
+ return factory.UpdateShorthandPropertyAssignment(
173
+ assignment,
174
+ assignment.Modifiers(),
175
+ name,
176
+ assignment.PostfixToken,
177
+ nil,
178
+ nil,
179
+ visitor.VisitNode(assignment.ObjectAssignmentInitializer),
180
+ )
181
+ case shimast.KindPropertyAccessExpression:
182
+ access := current.AsPropertyAccessExpression()
183
+ return factory.UpdatePropertyAccessExpression(
184
+ access,
185
+ visitor.VisitNode(access.Expression),
186
+ access.QuestionDotToken,
187
+ access.Name(),
188
+ access.Flags,
189
+ )
190
+ default:
191
+ return visitor.VisitEachChild(current)
192
+ }
193
+ }, factory, shimast.NodeVisitorHooks{})
194
+ return visitor.VisitNode(node)
195
+ }
196
+
197
+ func rewriteNestiaIdentifierExpression(
198
+ factory *shimast.NodeFactory,
199
+ replacement string,
200
+ ) *shimast.Node {
201
+ parts := []string{}
202
+ start := 0
203
+ for i := 0; i <= len(replacement); i++ {
204
+ if i == len(replacement) || replacement[i] == '.' {
205
+ if start < i {
206
+ parts = append(parts, replacement[start:i])
207
+ }
208
+ start = i + 1
209
+ }
210
+ }
211
+ if len(parts) == 0 {
212
+ return factory.NewIdentifier(replacement)
213
+ }
214
+ output := factory.NewIdentifier(parts[0])
215
+ for _, part := range parts[1:] {
216
+ output = factory.NewPropertyAccessExpression(
217
+ output,
218
+ nil,
219
+ factory.NewIdentifier(part),
220
+ shimast.NodeFlagsNone,
221
+ )
222
+ }
223
+ return output
224
+ }
225
+
226
+ func normalizeNestiaSyntheticTokens(node *shimast.Node) {
227
+ if node == nil {
228
+ return
229
+ }
230
+ if node.Kind == shimast.KindConditionalExpression {
231
+ conditional := node.AsConditionalExpression()
232
+ factory := shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
233
+ if conditional.QuestionToken == nil {
234
+ conditional.QuestionToken = factory.NewToken(shimast.KindQuestionToken)
235
+ }
236
+ if conditional.ColonToken == nil {
237
+ conditional.ColonToken = factory.NewToken(nestiaKindColonToken)
238
+ }
239
+ }
240
+ node.ForEachChild(func(child *shimast.Node) bool {
241
+ normalizeNestiaSyntheticTokens(child)
242
+ return false
243
+ })
244
+ }