ttsc 0.3.0 → 0.4.0-dev.20260424

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 (112) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +216 -302
  3. package/cmd/ttsc/build.go +128 -0
  4. package/cmd/ttsc/main.go +222 -0
  5. package/cmd/ttsc/main_test.go +288 -0
  6. package/cmd/ttsc/transform.go +166 -0
  7. package/driver/host.go +31 -0
  8. package/driver/program.go +298 -0
  9. package/driver/rewrite.go +315 -0
  10. package/go.mod +48 -0
  11. package/go.sum +22 -0
  12. package/lib/api.d.ts +91 -0
  13. package/lib/api.js +325 -0
  14. package/lib/api.js.map +1 -0
  15. package/lib/cli.d.ts +1 -0
  16. package/lib/cli.js +481 -0
  17. package/lib/cli.js.map +1 -0
  18. package/lib/index.d.ts +17 -0
  19. package/lib/index.js +34 -0
  20. package/lib/index.js.map +1 -0
  21. package/lib/native.d.ts +15 -0
  22. package/lib/native.js +34 -0
  23. package/lib/native.js.map +1 -0
  24. package/lib/platform.d.ts +66 -0
  25. package/lib/platform.js +170 -0
  26. package/lib/platform.js.map +1 -0
  27. package/lib/plugin.d.ts +47 -0
  28. package/lib/plugin.js +150 -0
  29. package/lib/plugin.js.map +1 -0
  30. package/lib/project.d.ts +32 -0
  31. package/lib/project.js +303 -0
  32. package/lib/project.js.map +1 -0
  33. package/package.json +70 -63
  34. package/shim/ast/extra-shim.json +4 -0
  35. package/shim/ast/go.mod +5 -0
  36. package/shim/ast/shim.go +117 -0
  37. package/shim/bundled/extra-shim.json +1 -0
  38. package/shim/bundled/go.mod +5 -0
  39. package/shim/bundled/shim.go +19 -0
  40. package/shim/checker/extra-shim.json +31 -0
  41. package/shim/checker/go.mod +5 -0
  42. package/shim/checker/shim.go +146 -0
  43. package/shim/compiler/extra-shim.json +4 -0
  44. package/shim/compiler/go.mod +5 -0
  45. package/shim/compiler/shim.go +50 -0
  46. package/shim/core/extra-shim.json +4 -0
  47. package/shim/core/go.mod +5 -0
  48. package/shim/core/shim.go +11 -0
  49. package/shim/diagnosticwriter/go.mod +5 -0
  50. package/shim/diagnosticwriter/shim.go +29 -0
  51. package/shim/parser/extra-shim.json +4 -0
  52. package/shim/parser/go.mod +5 -0
  53. package/shim/parser/shim.go +55 -0
  54. package/shim/scanner/extra-shim.json +4 -0
  55. package/shim/scanner/go.mod +5 -0
  56. package/shim/scanner/shim.go +96 -0
  57. package/shim/tsoptions/extra-shim.json +4 -0
  58. package/shim/tsoptions/go.mod +5 -0
  59. package/shim/tsoptions/shim.go +123 -0
  60. package/shim/tspath/extra-shim.json +4 -0
  61. package/shim/tspath/go.mod +5 -0
  62. package/shim/tspath/shim.go +165 -0
  63. package/shim/vfs/cachedvfs/extra-shim.json +4 -0
  64. package/shim/vfs/cachedvfs/go.mod +5 -0
  65. package/shim/vfs/cachedvfs/shim.go +12 -0
  66. package/shim/vfs/extra-shim.json +4 -0
  67. package/shim/vfs/go.mod +5 -0
  68. package/shim/vfs/osvfs/extra-shim.json +4 -0
  69. package/shim/vfs/osvfs/go.mod +5 -0
  70. package/shim/vfs/osvfs/shim.go +13 -0
  71. package/shim/vfs/shim.go +19 -0
  72. package/src/api.ts +416 -0
  73. package/src/cli.ts +492 -0
  74. package/src/index.ts +18 -0
  75. package/src/launcher/ttsc-dev.js +42 -0
  76. package/src/launcher/ttsc.js +19 -0
  77. package/src/native.ts +60 -0
  78. package/src/platform.ts +163 -0
  79. package/src/plugin.ts +241 -0
  80. package/src/project.ts +330 -0
  81. package/test/native.test.ts +60 -0
  82. package/test/package.test.ts +123 -0
  83. package/test/platform.test.ts +58 -0
  84. package/test/project.test.ts +195 -0
  85. package/tools/gen_shims/go.mod +14 -0
  86. package/tools/gen_shims/go.sum +12 -0
  87. package/tools/gen_shims/main.go +446 -0
  88. package/CHANGELOG.md +0 -2
  89. package/bin/tsc +0 -2
  90. package/bin/tsserver +0 -2
  91. package/lib/PluginCreator.d.ts +0 -67
  92. package/lib/PluginCreator.js +0 -190
  93. package/lib/cancelationToken.d.ts +0 -0
  94. package/lib/cancelationToken.js +0 -2
  95. package/lib/loadTypescript.d.ts +0 -7
  96. package/lib/loadTypescript.js +0 -56
  97. package/lib/patchCreateProgram.d.ts +0 -21
  98. package/lib/patchCreateProgram.js +0 -184
  99. package/lib/tsc.d.ts +0 -1
  100. package/lib/tsc.js +0 -24
  101. package/lib/tsserver.d.ts +0 -0
  102. package/lib/tsserver.js +0 -2
  103. package/lib/tsserverlibrary.d.ts +0 -3
  104. package/lib/tsserverlibrary.js +0 -4
  105. package/lib/typescript.d.ts +0 -3
  106. package/lib/typescript.js +0 -4
  107. package/lib/typescriptServices.d.ts +0 -3
  108. package/lib/typescriptServices.js +0 -4
  109. package/lib/typingsInstaller.d.ts +0 -0
  110. package/lib/typingsInstaller.js +0 -2
  111. package/lib/watchGuard.d.ts +0 -0
  112. package/lib/watchGuard.js +0 -2
@@ -0,0 +1,166 @@
1
+ package main
2
+
3
+ import (
4
+ "bytes"
5
+ "flag"
6
+ "fmt"
7
+ "os"
8
+ "path/filepath"
9
+ "strings"
10
+
11
+ shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
12
+
13
+ "github.com/samchon/ttsc/packages/ttsc/driver"
14
+ )
15
+
16
+ // runTransform implements `ttsc transform` — a single-file transform used by
17
+ // bundler plugin adapters (unplugin, vite, webpack, rollup, esbuild, rspack,
18
+ // farm). Behavior:
19
+ //
20
+ // - Loads the enclosing tsconfig so tsgo resolves imports and types.
21
+ // - Runs tsgo Emit, filtering WriteFile so only the requested source
22
+ // file's JS leaves the binary.
23
+ // - Applies the ttsc rewrite + import cleanup just like `build --emit`.
24
+ // - Writes the result to stdout (or `--out` when provided).
25
+ //
26
+ // The contract matches a per-file bundler transform hook: bundlers
27
+ // hand the plugin a source file path, the plugin shells out here, and the
28
+ // returned text replaces the file in the bundle graph.
29
+ func runTransform(args []string) int {
30
+ fs := flag.NewFlagSet("transform", flag.ContinueOnError)
31
+ fs.SetOutput(stderr)
32
+ file := fs.String("file", "", "absolute or cwd-relative path of the .ts file to transform")
33
+ tsconfigPath := fs.String("tsconfig", "tsconfig.json", "tsconfig.json owning --file")
34
+ cwdOverride := fs.String("cwd", "", "override the working directory")
35
+ out := fs.String("out", "", "write output JS to PATH (default: stdout)")
36
+ rewriteMode := fs.String("rewrite-mode", "none", "native rewrite backend id")
37
+ if err := fs.Parse(args); err != nil {
38
+ return 2
39
+ }
40
+ if *file == "" {
41
+ fmt.Fprintln(stderr, "ttsc transform: --file is required")
42
+ return 2
43
+ }
44
+
45
+ cwd := *cwdOverride
46
+ if cwd == "" {
47
+ var err error
48
+ cwd, err = os.Getwd()
49
+ if err != nil {
50
+ fmt.Fprintf(stderr, "ttsc transform: cwd: %v\n", err)
51
+ return 2
52
+ }
53
+ }
54
+
55
+ prog, diags, err := driver.LoadProgram(cwd, *tsconfigPath, driver.LoadProgramOptions{
56
+ ForceEmit: true,
57
+ })
58
+ if err != nil {
59
+ fmt.Fprintf(stderr, "ttsc transform: %v\n", err)
60
+ return 2
61
+ }
62
+ if len(diags) > 0 {
63
+ driver.WritePrettyDiagnostics(stderr, diags, cwd)
64
+ return 2
65
+ }
66
+ defer prog.Close()
67
+ if diags := prog.Diagnostics(); len(diags) > 0 {
68
+ driver.WritePrettyDiagnostics(stderr, diags, cwd)
69
+ return 2
70
+ }
71
+
72
+ absFile := *file
73
+ if !filepath.IsAbs(absFile) {
74
+ absFile = filepath.Join(cwd, absFile)
75
+ }
76
+ absFile = filepath.ToSlash(absFile)
77
+
78
+ rewrites := driver.NewRewriteSet()
79
+ if *rewriteMode != "none" {
80
+ fmt.Fprintf(stderr, "ttsc transform: rewrite backend %q is not built into the standalone ttsc binary\n", *rewriteMode)
81
+ fmt.Fprintln(stderr, "ttsc transform: run through the JS host with the matching compiler plugin so it can select the consumer-provided native binary")
82
+ return 2
83
+ }
84
+
85
+ target := prog.SourceFile(absFile)
86
+ if target == nil {
87
+ fmt.Fprintf(stderr, "ttsc transform: source file is not in program: %s\n", absFile)
88
+ return 2
89
+ }
90
+
91
+ // Capture the requested source file's JS output.
92
+ var captured []byte
93
+ capture := func(name, text string, _ *shimcompiler.WriteFileData) error {
94
+ rel := filepath.ToSlash(name)
95
+ if !isJavaScriptOutput(rel) {
96
+ return nil
97
+ }
98
+ captured = []byte(text)
99
+ return nil
100
+ }
101
+ _, eDiags, err := prog.EmitFile(rewrites, target, capture)
102
+ if err != nil {
103
+ fmt.Fprintf(stderr, "ttsc transform: emit: %v\n", err)
104
+ return 3
105
+ }
106
+ for _, d := range eDiags {
107
+ fmt.Fprintln(stderr, " -", d.String())
108
+ }
109
+ if captured == nil {
110
+ fmt.Fprintf(stderr, "ttsc transform: no output produced for %s\n", absFile)
111
+ return 3
112
+ }
113
+ if *out == "" {
114
+ if _, werr := bytes.NewReader(captured).WriteTo(stdout); werr != nil {
115
+ fmt.Fprintf(stderr, "ttsc transform: write stdout: %v\n", werr)
116
+ return 3
117
+ }
118
+ return 0
119
+ }
120
+ if dir := filepath.Dir(*out); dir != "" {
121
+ if err := os.MkdirAll(dir, 0o755); err != nil {
122
+ fmt.Fprintf(stderr, "ttsc transform: mkdir: %v\n", err)
123
+ return 3
124
+ }
125
+ }
126
+ if err := os.WriteFile(*out, captured, 0o644); err != nil {
127
+ fmt.Fprintf(stderr, "ttsc transform: write %s: %v\n", *out, err)
128
+ return 3
129
+ }
130
+ return 0
131
+ }
132
+
133
+ // sharedSourceStemSegments counts the shared trailing path segments between the
134
+ // emitted output path and the original source path after stripping extensions.
135
+ // `src/foo/bar.ts` ↔ `dist/foo/bar.js` returns 2, while unrelated paths return
136
+ // 0. `ttsc transform` uses the highest score to pick the emitted JS that
137
+ // belongs to the requested source file even when a project contains multiple
138
+ // `index.ts` files.
139
+ func sharedSourceStemSegments(outPath, srcPath string) int {
140
+ trim := func(p string) []string {
141
+ p = strings.TrimSuffix(p, filepath.Ext(p))
142
+ return strings.Split(p, "/")
143
+ }
144
+ a, b := trim(outPath), trim(srcPath)
145
+ n := len(a)
146
+ if len(b) < n {
147
+ n = len(b)
148
+ }
149
+ shared := 0
150
+ for i := 1; i <= n; i++ {
151
+ if a[len(a)-i] != b[len(b)-i] {
152
+ break
153
+ }
154
+ shared++
155
+ }
156
+ return shared
157
+ }
158
+
159
+ func isJavaScriptOutput(name string) bool {
160
+ switch strings.ToLower(filepath.Ext(name)) {
161
+ case ".js", ".mjs", ".cjs":
162
+ return true
163
+ default:
164
+ return false
165
+ }
166
+ }
package/driver/host.go ADDED
@@ -0,0 +1,31 @@
1
+ // Package driver wraps the typescript-go compiler so the rest of ttsc doesn't
2
+ // need to know about shim packages directly. It is the only part of the engine
3
+ // that imports `shim/*` by design — everything downstream consumes a small,
4
+ // shim-agnostic interface (`*Program`).
5
+ //
6
+ // Structural note: this file is deliberately adapted from tsgonest's
7
+ // `internal/compiler/host.go` (MIT, github.com/tsgonest/tsgonest).
8
+ // The helper surface is the same; we keep names in sync to make future
9
+ // upstream changes easy to compare.
10
+ package driver
11
+
12
+ import (
13
+ "github.com/microsoft/typescript-go/shim/bundled"
14
+ shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
15
+ "github.com/microsoft/typescript-go/shim/vfs"
16
+ "github.com/microsoft/typescript-go/shim/vfs/cachedvfs"
17
+ "github.com/microsoft/typescript-go/shim/vfs/osvfs"
18
+ )
19
+
20
+ // DefaultFS returns an OS-backed filesystem wrapped with tsgo's bundled libs so
21
+ // built-in definitions (lib.es*.d.ts, dom, etc.) resolve without a network
22
+ // fetch. Mirrors tsgonest/tsgolint behavior.
23
+ func DefaultFS() vfs.FS {
24
+ return bundled.WrapFS(cachedvfs.From(osvfs.FS()))
25
+ }
26
+
27
+ // DefaultHost returns a CompilerHost anchored at cwd that can find tsgo's
28
+ // bundled library files via `bundled.LibPath()`.
29
+ func DefaultHost(cwd string, fs vfs.FS) shimcompiler.CompilerHost {
30
+ return shimcompiler.NewCompilerHost(cwd, fs, bundled.LibPath(), nil, nil)
31
+ }
@@ -0,0 +1,298 @@
1
+ package driver
2
+
3
+ import (
4
+ "context"
5
+ "errors"
6
+ "fmt"
7
+ "io"
8
+ "path/filepath"
9
+
10
+ "github.com/microsoft/typescript-go/shim/ast"
11
+ shimchecker "github.com/microsoft/typescript-go/shim/checker"
12
+ shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
13
+ "github.com/microsoft/typescript-go/shim/core"
14
+ shimdiagnosticwriter "github.com/microsoft/typescript-go/shim/diagnosticwriter"
15
+ shimscanner "github.com/microsoft/typescript-go/shim/scanner"
16
+ "github.com/microsoft/typescript-go/shim/tsoptions"
17
+ "github.com/microsoft/typescript-go/shim/tspath"
18
+ "github.com/microsoft/typescript-go/shim/vfs"
19
+ )
20
+
21
+ // Diagnostic is the compilation diagnostic shape ttsc passes around. Kept
22
+ // dependency-free (no shim types) so callers can render or inspect freely.
23
+ type Diagnostic struct {
24
+ File string
25
+ Line int
26
+ Column int
27
+ Message string
28
+ raw *ast.Diagnostic
29
+ }
30
+
31
+ // SourceFile returns the program source file matching filename.
32
+ func (p *Program) SourceFile(filename string) *ast.SourceFile {
33
+ if p == nil || p.TSProgram == nil {
34
+ return nil
35
+ }
36
+ normalized := filepath.ToSlash(filename)
37
+ for _, file := range p.TSProgram.SourceFiles() {
38
+ if filepath.ToSlash(file.FileName()) == normalized {
39
+ return file
40
+ }
41
+ }
42
+ return nil
43
+ }
44
+
45
+ // String returns a `path:line:col: message` formatted string.
46
+ func (d Diagnostic) String() string {
47
+ if d.File == "" {
48
+ return d.Message
49
+ }
50
+ if d.Line > 0 {
51
+ return fmt.Sprintf("%s:%d:%d: %s", d.File, d.Line, d.Column, d.Message)
52
+ }
53
+ return fmt.Sprintf("%s: %s", d.File, d.Message)
54
+ }
55
+
56
+ // WritePrettyDiagnostics renders diagnostics with TypeScript-style colors,
57
+ // source snippets and the trailing error summary when raw tsgo diagnostic
58
+ // objects are available. It falls back to the legacy one-line form for
59
+ // diagnostics assembled outside tsgo.
60
+ func WritePrettyDiagnostics(w io.Writer, diagnostics []Diagnostic, cwd string) {
61
+ raw := make([]*ast.Diagnostic, 0, len(diagnostics))
62
+ for _, d := range diagnostics {
63
+ if d.raw == nil {
64
+ for _, d := range diagnostics {
65
+ fmt.Fprintln(w, " -", d.String())
66
+ }
67
+ return
68
+ }
69
+ raw = append(raw, d.raw)
70
+ }
71
+ shimdiagnosticwriter.FormatASTDiagnosticsWithColorAndContext(w, raw, cwd)
72
+ }
73
+
74
+ // Program is the shim-agnostic facade the rest of the engine sees.
75
+ type Program struct {
76
+ TSProgram *shimcompiler.Program
77
+ ParsedConfig *tsoptions.ParsedCommandLine
78
+ Checker *shimchecker.Checker
79
+ checkerRelease func()
80
+ Host shimcompiler.CompilerHost
81
+ FS vfs.FS
82
+ }
83
+
84
+ // LoadProgramOptions controls tsconfig overrides applied before tsgo creates
85
+ // the program. `ForceEmit` is used by `ttsc --emit` and `ttsc transform`
86
+ // so runtime compilation still works when the project defaults to `noEmit`.
87
+ type LoadProgramOptions struct {
88
+ ForceEmit bool
89
+ ForceNoEmit bool
90
+ OutDir string
91
+ }
92
+
93
+ // Close releases the checker pool lease acquired by LoadProgram.
94
+ func (p *Program) Close() error {
95
+ if p.checkerRelease != nil {
96
+ p.checkerRelease()
97
+ p.checkerRelease = nil
98
+ }
99
+ return nil
100
+ }
101
+
102
+ // ParseTSConfig parses a tsconfig.json file via tsgo's native JSONC parser.
103
+ // Comments, trailing commas, and `extends` chains are handled automatically.
104
+ //
105
+ // The absolute path is resolved against cwd before any VFS lookups because
106
+ // tsgo's filesystem APIs require absolute paths — mirrors what tsc does when
107
+ // you pass a relative `--project` flag.
108
+ func ParseTSConfig(fs vfs.FS, cwd, tsconfigPath string, host shimcompiler.CompilerHost) (*tsoptions.ParsedCommandLine, []Diagnostic, error) {
109
+ resolved := tspath.ResolvePath(cwd, tsconfigPath)
110
+ if !fs.FileExists(resolved) {
111
+ return nil, nil, fmt.Errorf("tsconfig not found: %s", resolved)
112
+ }
113
+ parsed, diags := tsoptions.GetParsedCommandLineOfConfigFile(resolved, &core.CompilerOptions{}, nil, host, nil)
114
+ if len(diags) > 0 {
115
+ return nil, convertDiagnostics(diags), nil
116
+ }
117
+ if parsed == nil {
118
+ return nil, nil, errors.New("tsoptions: parsed command line was nil")
119
+ }
120
+ if len(parsed.Errors) > 0 {
121
+ return nil, convertDiagnostics(parsed.Errors), nil
122
+ }
123
+ return parsed, nil, nil
124
+ }
125
+
126
+ // CreateProgramFromConfig builds a tsgo Program from the parsed config.
127
+ func CreateProgramFromConfig(parsed *tsoptions.ParsedCommandLine, host shimcompiler.CompilerHost) (*shimcompiler.Program, []Diagnostic, error) {
128
+ opts := shimcompiler.ProgramOptions{
129
+ Config: parsed,
130
+ SingleThreaded: core.TSTrue,
131
+ Host: host,
132
+ UseSourceOfProjectReference: true,
133
+ }
134
+ p := shimcompiler.NewProgram(opts)
135
+ if p == nil {
136
+ return nil, nil, errors.New("shimcompiler.NewProgram returned nil")
137
+ }
138
+ return p, nil, nil
139
+ }
140
+
141
+ // LoadProgram is the one-shot convenience used by `ttsc`.
142
+ // It parses the tsconfig, creates a program and a type-checker, and returns
143
+ // the wrapped facade.
144
+ //
145
+ // cwd must be absolute; tsconfigPath may be relative to cwd.
146
+ func LoadProgram(cwd, tsconfigPath string, options LoadProgramOptions) (*Program, []Diagnostic, error) {
147
+ if !filepath.IsAbs(cwd) {
148
+ if abs, err := filepath.Abs(cwd); err == nil {
149
+ cwd = abs
150
+ }
151
+ }
152
+ cwd = tspath.ResolvePath(cwd)
153
+ fs := DefaultFS()
154
+ host := DefaultHost(cwd, fs)
155
+
156
+ parsed, diags, err := ParseTSConfig(fs, cwd, tsconfigPath, host)
157
+ if err != nil {
158
+ return nil, nil, err
159
+ }
160
+ if len(diags) > 0 {
161
+ return nil, diags, nil
162
+ }
163
+ if options.ForceNoEmit {
164
+ forceNoEmit(parsed)
165
+ }
166
+ if options.ForceEmit {
167
+ forceEmit(parsed)
168
+ }
169
+ if options.OutDir != "" {
170
+ overrideOutDir(cwd, parsed, options.OutDir)
171
+ }
172
+
173
+ tsProgram, progDiags, err := CreateProgramFromConfig(parsed, host)
174
+ if err != nil {
175
+ return nil, nil, err
176
+ }
177
+ if len(progDiags) > 0 {
178
+ return nil, progDiags, nil
179
+ }
180
+
181
+ checker, done := tsProgram.GetTypeChecker(context.Background())
182
+ return &Program{
183
+ TSProgram: tsProgram,
184
+ ParsedConfig: parsed,
185
+ Checker: checker,
186
+ checkerRelease: done,
187
+ Host: host,
188
+ FS: fs,
189
+ }, nil, nil
190
+ }
191
+
192
+ func forceEmit(parsed *tsoptions.ParsedCommandLine) {
193
+ if parsed == nil || parsed.ParsedConfig == nil || parsed.ParsedConfig.CompilerOptions == nil {
194
+ return
195
+ }
196
+ options := parsed.ParsedConfig.CompilerOptions
197
+ options.NoEmit = core.TSFalse
198
+ options.EmitDeclarationOnly = core.TSFalse
199
+ }
200
+
201
+ func forceNoEmit(parsed *tsoptions.ParsedCommandLine) {
202
+ if parsed == nil || parsed.ParsedConfig == nil || parsed.ParsedConfig.CompilerOptions == nil {
203
+ return
204
+ }
205
+ parsed.ParsedConfig.CompilerOptions.NoEmit = core.TSTrue
206
+ }
207
+
208
+ func overrideOutDir(cwd string, parsed *tsoptions.ParsedCommandLine, outDir string) {
209
+ if parsed == nil || parsed.ParsedConfig == nil || parsed.ParsedConfig.CompilerOptions == nil {
210
+ return
211
+ }
212
+ parsed.ParsedConfig.CompilerOptions.OutDir = tspath.ResolvePath(cwd, outDir)
213
+ }
214
+
215
+ // SourceFiles exposes the program's user-authored source files (declaration
216
+ // files filtered out).
217
+ func (p *Program) SourceFiles() []*ast.SourceFile {
218
+ out := make([]*ast.SourceFile, 0)
219
+ for _, f := range p.TSProgram.SourceFiles() {
220
+ if f.IsDeclarationFile {
221
+ continue
222
+ }
223
+ out = append(out, f)
224
+ }
225
+ return out
226
+ }
227
+
228
+ // Diagnostics returns project diagnostics that must block compilation or
229
+ // runtime execution before any JavaScript is emitted or evaluated.
230
+ func (p *Program) Diagnostics() []Diagnostic {
231
+ if p == nil || p.TSProgram == nil {
232
+ return []Diagnostic{{Message: "driver: nil program"}}
233
+ }
234
+ ctx := context.Background()
235
+ raw := shimcompiler.GetDiagnosticsOfAnyProgram(
236
+ ctx,
237
+ p.TSProgram,
238
+ nil,
239
+ false,
240
+ p.TSProgram.GetBindDiagnostics,
241
+ p.TSProgram.GetSemanticDiagnostics,
242
+ )
243
+ raw = filterDiagnostics(raw)
244
+ return convertDiagnostics(shimcompiler.SortAndDeduplicateDiagnostics(raw))
245
+ }
246
+
247
+ func filterDiagnostics(in []*ast.Diagnostic) []*ast.Diagnostic {
248
+ out := in[:0]
249
+ for _, d := range in {
250
+ if isUnusedOverloadSignatureTypeParameterDiagnostic(d) {
251
+ continue
252
+ }
253
+ out = append(out, d)
254
+ }
255
+ return out
256
+ }
257
+
258
+ func isUnusedOverloadSignatureTypeParameterDiagnostic(d *ast.Diagnostic) bool {
259
+ if d == nil || d.File() == nil {
260
+ return false
261
+ }
262
+ switch d.Code() {
263
+ case 6196, 6205: // unused declaration / all type parameters are unused
264
+ default:
265
+ return false
266
+ }
267
+ node := ast.GetNodeAtPosition(d.File(), d.Pos(), false)
268
+ for node != nil {
269
+ if node.Kind == ast.KindFunctionDeclaration {
270
+ return node.Body() == nil
271
+ }
272
+ node = node.Parent
273
+ }
274
+ return false
275
+ }
276
+
277
+ // convertDiagnostics translates shim-specific diagnostics into the plain
278
+ // Diagnostic struct with line/column populated via tsgo's ECMALineMap (the
279
+ // same helper tsc uses for its "file:line:col: message" banner).
280
+ func convertDiagnostics(in []*ast.Diagnostic) []Diagnostic {
281
+ out := make([]Diagnostic, 0, len(in))
282
+ for _, d := range in {
283
+ if d == nil {
284
+ continue
285
+ }
286
+ diag := Diagnostic{Message: d.String(), raw: d}
287
+ if file := d.File(); file != nil {
288
+ diag.File = file.FileName()
289
+ if pos := d.Pos(); pos >= 0 {
290
+ line, col := shimscanner.GetECMALineAndByteOffsetOfPosition(file, pos)
291
+ diag.Line = line + 1
292
+ diag.Column = col + 1
293
+ }
294
+ }
295
+ out = append(out, diag)
296
+ }
297
+ return out
298
+ }