ttsc 0.12.3 → 0.12.4
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/cmd/platform/main.go +9 -0
- package/cmd/ttsc/api_compile.go +109 -101
- package/cmd/ttsc/api_compile_branches_test.go +55 -57
- package/cmd/ttsc/api_transform.go +54 -49
- package/cmd/ttsc/api_transform_branches_test.go +30 -32
- package/cmd/ttsc/build.go +111 -108
- package/cmd/ttsc/build_branches_test.go +95 -97
- package/cmd/ttsc/helpers_test.go +39 -28
- package/cmd/ttsc/main.go +111 -104
- package/cmd/ttsc/main_alias_branches_test.go +21 -23
- package/cmd/ttscserver/main.go +96 -90
- package/cmd/ttscserver/run_lsp_prefers_tsgo_flag_test.go +22 -22
- package/cmd/ttscserver/run_lsp_reports_server_error_test.go +22 -22
- package/cmd/ttscserver/run_lsp_uses_tsgo_environment_test.go +23 -23
- package/cmd/utility-host/main.go +30 -23
- package/driver/lsp.go +53 -7
- package/driver/plugins.go +88 -72
- package/driver/program.go +362 -339
- package/driver/rewrite.go +442 -380
- package/internal/lspserver/lsp_envelope.go +64 -64
- package/internal/lspserver/lsp_frame.go +60 -60
- package/internal/lspserver/lsp_plugin.go +57 -57
- package/internal/lspserver/lsp_proxy.go +265 -251
- package/internal/lspserver/lsp_server.go +155 -144
- package/lib/TtscCompiler.d.ts +7 -0
- package/lib/TtscCompiler.js +7 -0
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/buildNativeCompiler.d.ts +11 -1
- package/lib/compiler/internal/buildNativeCompiler.js +23 -3
- package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.d.ts +9 -0
- package/lib/compiler/internal/compileProjectInMemory.js +36 -21
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/paths.d.ts +27 -0
- package/lib/compiler/internal/paths.js +70 -0
- package/lib/compiler/internal/paths.js.map +1 -0
- package/lib/compiler/internal/project/readProjectConfig.d.ts +9 -1
- package/lib/compiler/internal/project/readProjectConfig.js +80 -1
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/project/resolveProjectConfig.d.ts +13 -1
- package/lib/compiler/internal/project/resolveProjectConfig.js +24 -1
- package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
- package/lib/compiler/internal/resolveBinary.d.ts +9 -1
- package/lib/compiler/internal/resolveBinary.js +17 -1
- package/lib/compiler/internal/resolveBinary.js.map +1 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.d.ts +16 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.js +42 -10
- package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
- package/lib/compiler/internal/resolveTsgo.d.ts +21 -1
- package/lib/compiler/internal/resolveTsgo.js +23 -1
- package/lib/compiler/internal/resolveTsgo.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +19 -0
- package/lib/compiler/internal/runBuild.js +112 -46
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.d.ts +10 -1
- package/lib/compiler/internal/runSingleFileEmit.js +14 -1
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +5 -0
- package/lib/compiler/internal/sharedHostHelpers.js +6 -2
- package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
- package/lib/compiler/internal/spawnNative.d.ts +21 -0
- package/lib/compiler/internal/spawnNative.js +59 -0
- package/lib/compiler/internal/spawnNative.js.map +1 -0
- package/lib/compiler/internal/transformProjectInMemory.d.ts +15 -1
- package/lib/compiler/internal/transformProjectInMemory.js +57 -61
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/launcher/internal/getCompilerVersionText.js +7 -0
- package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.js +32 -0
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/resolveCacheDir.d.ts +9 -0
- package/lib/launcher/internal/resolveCacheDir.js +22 -0
- package/lib/launcher/internal/resolveCacheDir.js.map +1 -0
- package/lib/launcher/internal/resolveTtscserverBinary.js +2 -0
- package/lib/launcher/internal/resolveTtscserverBinary.js.map +1 -1
- package/lib/launcher/internal/runTtsc.d.ts +10 -0
- package/lib/launcher/internal/runTtsc.js +22 -9
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.js +10 -35
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/launcher/internal/runTtsx.d.ts +8 -0
- package/lib/launcher/internal/runTtsx.js +33 -7
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.d.ts +38 -0
- package/lib/plugin/internal/buildSourcePlugin.js +40 -17
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.d.ts +26 -0
- package/lib/plugin/internal/loadProjectPlugins.js +32 -18
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +15 -2
- package/lib/structures/internal/TtscSingleFileEmitOptions.d.ts +0 -2
- package/package.json +9 -9
- package/shim/ast/shim.go +24 -0
- package/shim/checker/shim.go +35 -0
- package/shim/core/shim.go +17 -0
- package/shim/diagnosticwriter/shim.go +4 -0
- package/shim/lsp/shim.go +3 -3
- package/shim/printer/shim.go +20 -0
- package/src/TtscCompiler.ts +7 -0
- package/src/compiler/internal/buildNativeCompiler.ts +23 -3
- package/src/compiler/internal/compileProjectInMemory.ts +34 -25
- package/src/compiler/internal/paths.ts +68 -0
- package/src/compiler/internal/project/readProjectConfig.ts +86 -1
- package/src/compiler/internal/project/resolveProjectConfig.ts +24 -1
- package/src/compiler/internal/resolveBinary.ts +17 -1
- package/src/compiler/internal/resolveEmittedJavaScript.ts +43 -12
- package/src/compiler/internal/resolveTsgo.ts +29 -1
- package/src/compiler/internal/runBuild.ts +110 -55
- package/src/compiler/internal/runSingleFileEmit.ts +14 -1
- package/src/compiler/internal/sharedHostHelpers.ts +7 -7
- package/src/compiler/internal/spawnNative.ts +64 -0
- package/src/compiler/internal/transformProjectInMemory.ts +47 -67
- package/src/launcher/internal/getCompilerVersionText.ts +7 -0
- package/src/launcher/internal/prepareExecution.ts +32 -0
- package/src/launcher/internal/resolveCacheDir.ts +19 -0
- package/src/launcher/internal/resolveTtscserverBinary.ts +2 -0
- package/src/launcher/internal/runTtsc.ts +22 -10
- package/src/launcher/internal/runTtscserver.ts +9 -1
- package/src/launcher/internal/runTtsx.ts +33 -8
- package/src/plugin/internal/buildSourcePlugin.ts +40 -14
- package/src/plugin/internal/loadProjectPlugins.ts +42 -18
- package/src/structures/ITtscCompilerTransformation.ts +15 -2
- package/src/structures/ITtscPlugin.ts +0 -1
- package/src/structures/internal/TtscSingleFileEmitOptions.ts +0 -2
- package/tools/gen_shims/main.go +43 -8
- package/utility/host.go +294 -245
package/driver/program.go
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
package driver
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
4
|
+
"context"
|
|
5
|
+
"fmt"
|
|
6
|
+
"io"
|
|
7
|
+
"path/filepath"
|
|
8
|
+
"strings"
|
|
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
19
|
)
|
|
20
20
|
|
|
21
21
|
// Diagnostic is the compilation diagnostic shape ttsc passes around. Kept
|
|
@@ -27,16 +27,16 @@ import (
|
|
|
27
27
|
// `@ttsc/lint`). At most one of `raw` / `lint` is non-nil; both nil falls
|
|
28
28
|
// back to the legacy single-line form.
|
|
29
29
|
type Diagnostic struct {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
File string
|
|
31
|
+
Line int
|
|
32
|
+
Column int
|
|
33
|
+
Code int32
|
|
34
|
+
Start *int
|
|
35
|
+
Length *int
|
|
36
|
+
Message string
|
|
37
|
+
Severity Severity
|
|
38
|
+
raw *ast.Diagnostic
|
|
39
|
+
lint *shimdiagnosticwriter.LintDiagnostic
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
// Severity classifies a diagnostic's blast radius. ttsc treats Error as a
|
|
@@ -44,12 +44,12 @@ type Diagnostic struct {
|
|
|
44
44
|
type Severity int
|
|
45
45
|
|
|
46
46
|
const (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
// SeverityError is the default for tsgo typecheck output and any
|
|
48
|
+
// plugin-emitted finding that should fail the build.
|
|
49
|
+
SeverityError Severity = iota
|
|
50
|
+
// SeverityWarning prints with warning coloring but keeps the build
|
|
51
|
+
// status at zero.
|
|
52
|
+
SeverityWarning
|
|
53
53
|
)
|
|
54
54
|
|
|
55
55
|
// IsError reports whether the diagnostic counts toward the build's error
|
|
@@ -63,60 +63,60 @@ func (d Diagnostic) IsError() bool { return d.Severity == SeverityError }
|
|
|
63
63
|
// rule's enum index). Severity controls both the rendered banner color and
|
|
64
64
|
// the exit-code outcome.
|
|
65
65
|
func NewLintDiagnostic(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
file *ast.SourceFile,
|
|
67
|
+
pos, end int,
|
|
68
|
+
code int32,
|
|
69
|
+
severity Severity,
|
|
70
|
+
message string,
|
|
71
71
|
) Diagnostic {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
72
|
+
cat := shimdiagnosticwriter.LintCategoryError
|
|
73
|
+
if severity == SeverityWarning {
|
|
74
|
+
cat = shimdiagnosticwriter.LintCategoryWarning
|
|
75
|
+
}
|
|
76
|
+
lint := shimdiagnosticwriter.NewLintDiagnostic(file, pos, end, code, cat, message)
|
|
77
|
+
d := Diagnostic{
|
|
78
|
+
Code: code,
|
|
79
|
+
Message: message,
|
|
80
|
+
Severity: severity,
|
|
81
|
+
lint: lint,
|
|
82
|
+
}
|
|
83
|
+
if file != nil {
|
|
84
|
+
d.File = file.FileName()
|
|
85
|
+
if pos >= 0 {
|
|
86
|
+
length := lint.Len()
|
|
87
|
+
d.Start = &pos
|
|
88
|
+
d.Length = &length
|
|
89
|
+
line, col := shimscanner.GetECMALineAndByteOffsetOfPosition(file, pos)
|
|
90
|
+
d.Line = line + 1
|
|
91
|
+
d.Column = col + 1
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return d
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
// SourceFile returns the program source file matching filename.
|
|
98
98
|
func (p *Program) SourceFile(filename string) *ast.SourceFile {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
99
|
+
if p == nil || p.TSProgram == nil {
|
|
100
|
+
return nil
|
|
101
|
+
}
|
|
102
|
+
normalized := filepath.ToSlash(filename)
|
|
103
|
+
for _, file := range p.TSProgram.SourceFiles() {
|
|
104
|
+
if filepath.ToSlash(file.FileName()) == normalized {
|
|
105
|
+
return file
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return nil
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
// String returns a `path:line:col: message` formatted string.
|
|
112
112
|
func (d Diagnostic) String() string {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
113
|
+
if d.File == "" {
|
|
114
|
+
return d.Message
|
|
115
|
+
}
|
|
116
|
+
if d.Line > 0 {
|
|
117
|
+
return fmt.Sprintf("%s:%d:%d: %s", d.File, d.Line, d.Column, d.Message)
|
|
118
|
+
}
|
|
119
|
+
return fmt.Sprintf("%s: %s", d.File, d.Message)
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
// WritePrettyDiagnostics renders diagnostics with TypeScript-style colors,
|
|
@@ -125,94 +125,94 @@ func (d Diagnostic) String() string {
|
|
|
125
125
|
// are rendered through the same color/context pipeline; entries without
|
|
126
126
|
// either anchor fall back to the legacy `path:line:col: message` form.
|
|
127
127
|
func WritePrettyDiagnostics(w io.Writer, diagnostics []Diagnostic, cwd string) {
|
|
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
|
-
|
|
128
|
+
if len(diagnostics) == 0 {
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
rich := make([]Diagnostic, 0, len(diagnostics))
|
|
132
|
+
plain := make([]Diagnostic, 0)
|
|
133
|
+
for _, d := range diagnostics {
|
|
134
|
+
if d.raw != nil || d.lint != nil {
|
|
135
|
+
rich = append(rich, d)
|
|
136
|
+
} else {
|
|
137
|
+
plain = append(plain, d)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if len(rich) > 0 {
|
|
141
|
+
astDiags := make([]*ast.Diagnostic, 0, len(rich))
|
|
142
|
+
lintDiags := make([]*shimdiagnosticwriter.LintDiagnostic, 0, len(rich))
|
|
143
|
+
for _, d := range rich {
|
|
144
|
+
if d.raw != nil {
|
|
145
|
+
astDiags = append(astDiags, d.raw)
|
|
146
|
+
}
|
|
147
|
+
if d.lint != nil {
|
|
148
|
+
lintDiags = append(lintDiags, d.lint)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
shimdiagnosticwriter.FormatMixedDiagnostics(w, astDiags, lintDiags, cwd)
|
|
152
|
+
}
|
|
153
|
+
for _, d := range plain {
|
|
154
|
+
fmt.Fprintln(w, " -", d.String())
|
|
155
|
+
}
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
// CountErrors returns the number of diagnostics that should fail the build.
|
|
159
159
|
// tsgo diagnostics carry their own `Error` category; lint diagnostics carry a
|
|
160
160
|
// caller-set Severity. Anything that isn't an explicit warning counts.
|
|
161
161
|
func CountErrors(diagnostics []Diagnostic) int {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
162
|
+
n := 0
|
|
163
|
+
for _, d := range diagnostics {
|
|
164
|
+
if d.lint != nil {
|
|
165
|
+
if d.lint.IsError() {
|
|
166
|
+
n++
|
|
167
|
+
}
|
|
168
|
+
continue
|
|
169
|
+
}
|
|
170
|
+
if d.raw != nil {
|
|
171
|
+
// tsgo diagnostics use the diagnostics package category. The
|
|
172
|
+
// renderer shim already mirrors the same Error/Warning split, so
|
|
173
|
+
// re-categorize via the public IsError shortcut.
|
|
174
|
+
if d.Severity != SeverityWarning {
|
|
175
|
+
n++
|
|
176
|
+
}
|
|
177
|
+
continue
|
|
178
|
+
}
|
|
179
|
+
// Plain text diagnostics (manually assembled): treat as errors so
|
|
180
|
+
// "ttsc: tsconfig not found"-style failures still flip the exit code.
|
|
181
|
+
n++
|
|
182
|
+
}
|
|
183
|
+
return n
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
// Program is the shim-agnostic facade the rest of the engine sees.
|
|
187
187
|
type Program struct {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
188
|
+
TSProgram *shimcompiler.Program
|
|
189
|
+
ParsedConfig *tsoptions.ParsedCommandLine
|
|
190
|
+
Checker *shimchecker.Checker
|
|
191
|
+
checkerRelease func()
|
|
192
|
+
Host shimcompiler.CompilerHost
|
|
193
|
+
FS vfs.FS
|
|
194
|
+
SourcePreamble string
|
|
195
|
+
plugins linkedPluginState
|
|
196
|
+
pluginsApplied bool
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
// LoadProgramOptions controls tsconfig overrides applied before tsgo creates
|
|
200
200
|
// the program. `ForceEmit` is used by `ttsc --emit` and runtime compilation
|
|
201
201
|
// so execution still works when the project defaults to `noEmit`.
|
|
202
202
|
type LoadProgramOptions struct {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
203
|
+
ForceEmit bool
|
|
204
|
+
ForceNoEmit bool
|
|
205
|
+
OutDir string
|
|
206
|
+
SourcePreamble string
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
// Close releases the checker pool lease acquired by LoadProgram.
|
|
210
210
|
func (p *Program) Close() error {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
211
|
+
if p.checkerRelease != nil {
|
|
212
|
+
p.checkerRelease()
|
|
213
|
+
p.checkerRelease = nil
|
|
214
|
+
}
|
|
215
|
+
return nil
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
// ParseTSConfig parses a tsconfig.json file via tsgo's native JSONC parser.
|
|
@@ -222,31 +222,31 @@ func (p *Program) Close() error {
|
|
|
222
222
|
// tsgo's filesystem APIs require absolute paths — mirrors what tsc does when
|
|
223
223
|
// you pass a relative `--project` flag.
|
|
224
224
|
func ParseTSConfig(fs vfs.FS, cwd, tsconfigPath string, host shimcompiler.CompilerHost) (*tsoptions.ParsedCommandLine, []Diagnostic, error) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
225
|
+
resolved := tspath.ResolvePath(cwd, tsconfigPath)
|
|
226
|
+
if !fs.FileExists(resolved) {
|
|
227
|
+
return nil, nil, fmt.Errorf("tsconfig not found: %s", resolved)
|
|
228
|
+
}
|
|
229
|
+
parsed, diags := tsoptions.GetParsedCommandLineOfConfigFile(resolved, &core.CompilerOptions{}, nil, host, nil)
|
|
230
|
+
allDiags := append(diags, parsed.Errors...)
|
|
231
|
+
if len(allDiags) > 0 {
|
|
232
|
+
return nil, convertDiagnostics(allDiags), nil
|
|
233
|
+
}
|
|
234
|
+
return parsed, nil, nil
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
// CreateProgramFromConfig builds a tsgo Program from the parsed config.
|
|
238
238
|
func CreateProgramFromConfig(parsed *tsoptions.ParsedCommandLine, host shimcompiler.CompilerHost) (*shimcompiler.Program, []Diagnostic, error) {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
239
|
+
if parsed == nil {
|
|
240
|
+
return nil, nil, fmt.Errorf("driver: nil parsed command line")
|
|
241
|
+
}
|
|
242
|
+
opts := shimcompiler.ProgramOptions{
|
|
243
|
+
Config: parsed,
|
|
244
|
+
SingleThreaded: core.TSTrue,
|
|
245
|
+
Host: host,
|
|
246
|
+
UseSourceOfProjectReference: true,
|
|
247
|
+
}
|
|
248
|
+
p := shimcompiler.NewProgram(opts)
|
|
249
|
+
return p, nil, nil
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
// LoadProgram is the one-shot convenience used by `ttsc`.
|
|
@@ -255,230 +255,253 @@ func CreateProgramFromConfig(parsed *tsoptions.ParsedCommandLine, host shimcompi
|
|
|
255
255
|
//
|
|
256
256
|
// cwd must be absolute; tsconfigPath may be relative to cwd.
|
|
257
257
|
func LoadProgram(cwd, tsconfigPath string, options LoadProgramOptions) (*Program, []Diagnostic, error) {
|
|
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
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
258
|
+
if !filepath.IsAbs(cwd) {
|
|
259
|
+
if abs, err := filepath.Abs(cwd); err == nil {
|
|
260
|
+
cwd = abs
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
cwd = tspath.ResolvePath(cwd)
|
|
264
|
+
pluginState, err := loadLinkedPluginState(cwd, tsconfigPath)
|
|
265
|
+
if err != nil {
|
|
266
|
+
return nil, nil, err
|
|
267
|
+
}
|
|
268
|
+
preamble, err := pluginState.sourcePreamble()
|
|
269
|
+
if err != nil {
|
|
270
|
+
return nil, nil, err
|
|
271
|
+
}
|
|
272
|
+
if preamble != "" {
|
|
273
|
+
options.SourcePreamble += preamble
|
|
274
|
+
}
|
|
275
|
+
fs := DefaultFS()
|
|
276
|
+
if options.SourcePreamble != "" {
|
|
277
|
+
fs = sourcePreambleFS{
|
|
278
|
+
FS: fs,
|
|
279
|
+
preamble: options.SourcePreamble,
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
host := DefaultHost(cwd, fs)
|
|
283
|
+
|
|
284
|
+
parsed, diags, err := ParseTSConfig(fs, cwd, tsconfigPath, host)
|
|
285
|
+
if err != nil {
|
|
286
|
+
return nil, nil, err
|
|
287
|
+
}
|
|
288
|
+
if len(diags) > 0 {
|
|
289
|
+
return nil, diags, nil
|
|
290
|
+
}
|
|
291
|
+
if options.ForceNoEmit {
|
|
292
|
+
forceNoEmit(parsed)
|
|
293
|
+
}
|
|
294
|
+
if options.ForceEmit {
|
|
295
|
+
forceEmit(parsed)
|
|
296
|
+
}
|
|
297
|
+
if options.OutDir != "" {
|
|
298
|
+
overrideOutDir(cwd, parsed, options.OutDir)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
tsProgram, _, _ := CreateProgramFromConfig(parsed, host)
|
|
302
|
+
|
|
303
|
+
checker, done := tsProgram.GetTypeChecker(context.Background())
|
|
304
|
+
prog := &Program{
|
|
305
|
+
TSProgram: tsProgram,
|
|
306
|
+
ParsedConfig: parsed,
|
|
307
|
+
Checker: checker,
|
|
308
|
+
checkerRelease: done,
|
|
309
|
+
Host: host,
|
|
310
|
+
FS: fs,
|
|
311
|
+
SourcePreamble: options.SourcePreamble,
|
|
312
|
+
}
|
|
313
|
+
prog.plugins = pluginState
|
|
314
|
+
return prog, nil, nil
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
// forceEmit clears noEmit and emitDeclarationOnly so the program always
|
|
318
|
+
// produces JavaScript output regardless of the tsconfig settings.
|
|
317
319
|
func forceEmit(parsed *tsoptions.ParsedCommandLine) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
320
|
+
options := parsed.ParsedConfig.CompilerOptions
|
|
321
|
+
options.NoEmit = core.TSFalse
|
|
322
|
+
options.EmitDeclarationOnly = core.TSFalse
|
|
321
323
|
}
|
|
322
324
|
|
|
325
|
+
// forceNoEmit sets noEmit so the program type-checks without writing files.
|
|
323
326
|
func forceNoEmit(parsed *tsoptions.ParsedCommandLine) {
|
|
324
|
-
|
|
327
|
+
parsed.ParsedConfig.CompilerOptions.NoEmit = core.TSTrue
|
|
325
328
|
}
|
|
326
329
|
|
|
330
|
+
// overrideOutDir resolves outDir against cwd and applies it to the parsed
|
|
331
|
+
// config, replacing any outDir already set in tsconfig.json.
|
|
327
332
|
func overrideOutDir(cwd string, parsed *tsoptions.ParsedCommandLine, outDir string) {
|
|
328
|
-
|
|
333
|
+
parsed.ParsedConfig.CompilerOptions.OutDir = tspath.ResolvePath(cwd, outDir)
|
|
329
334
|
}
|
|
330
335
|
|
|
336
|
+
// sourcePreambleFS wraps a vfs.FS and prepends the preamble string to every
|
|
337
|
+
// source file read by tsgo's parser. Declaration files (.d.ts etc.) are
|
|
338
|
+
// excluded so injected code never appears in type definitions.
|
|
331
339
|
type sourcePreambleFS struct {
|
|
332
|
-
|
|
333
|
-
|
|
340
|
+
vfs.FS
|
|
341
|
+
preamble string
|
|
334
342
|
}
|
|
335
343
|
|
|
336
344
|
func (fs sourcePreambleFS) ReadFile(filePath string) (string, bool) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
345
|
+
contents, ok := fs.FS.ReadFile(filePath)
|
|
346
|
+
if !ok || !isSourcePreambleTarget(filePath) {
|
|
347
|
+
return contents, ok
|
|
348
|
+
}
|
|
349
|
+
return ApplySourcePreamble(contents, fs.preamble), true
|
|
342
350
|
}
|
|
343
351
|
|
|
352
|
+
// isSourcePreambleTarget reports whether the preamble should be injected into
|
|
353
|
+
// the file at filePath. Declaration files are excluded; all other TypeScript
|
|
354
|
+
// and JavaScript source extensions qualify.
|
|
344
355
|
func isSourcePreambleTarget(filePath string) bool {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
356
|
+
lower := strings.ToLower(filepath.ToSlash(filePath))
|
|
357
|
+
for _, suffix := range []string{".d.ts", ".d.mts", ".d.cts"} {
|
|
358
|
+
if strings.HasSuffix(lower, suffix) {
|
|
359
|
+
return false
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
for _, suffix := range []string{".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"} {
|
|
363
|
+
if strings.HasSuffix(lower, suffix) {
|
|
364
|
+
return true
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return false
|
|
357
368
|
}
|
|
358
369
|
|
|
359
370
|
// ApplySourcePreamble inserts a generated source preamble without moving the
|
|
360
371
|
// file's BOM or hashbang away from the first bytes of the physical output.
|
|
361
372
|
func ApplySourcePreamble(text string, preamble string) string {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
373
|
+
if preamble == "" {
|
|
374
|
+
return text
|
|
375
|
+
}
|
|
376
|
+
bom := ""
|
|
377
|
+
rest := text
|
|
378
|
+
if strings.HasPrefix(rest, "\ufeff") {
|
|
379
|
+
bom = "\ufeff"
|
|
380
|
+
rest = strings.TrimPrefix(rest, "\ufeff")
|
|
381
|
+
}
|
|
382
|
+
if strings.HasPrefix(rest, "#!") {
|
|
383
|
+
end := strings.IndexByte(rest, '\n')
|
|
384
|
+
if end < 0 {
|
|
385
|
+
return bom + rest + "\n" + preamble
|
|
386
|
+
}
|
|
387
|
+
return bom + rest[:end+1] + preamble + rest[end+1:]
|
|
388
|
+
}
|
|
389
|
+
return bom + preamble + rest
|
|
379
390
|
}
|
|
380
391
|
|
|
381
392
|
// SourceFiles exposes the program's user-authored source files (declaration
|
|
382
393
|
// files filtered out).
|
|
383
394
|
func (p *Program) SourceFiles() []*ast.SourceFile {
|
|
384
|
-
|
|
385
|
-
|
|
395
|
+
_ = p.ApplyLinkedPlugins()
|
|
396
|
+
return p.sourceFilesRaw()
|
|
386
397
|
}
|
|
387
398
|
|
|
399
|
+
// sourceFilesRaw returns the program's non-declaration source files without
|
|
400
|
+
// running ApplyLinkedPlugins. Used internally to avoid a re-entrant apply.
|
|
388
401
|
func (p *Program) sourceFilesRaw() []*ast.SourceFile {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
402
|
+
out := make([]*ast.SourceFile, 0)
|
|
403
|
+
if p == nil || p.TSProgram == nil {
|
|
404
|
+
return out
|
|
405
|
+
}
|
|
406
|
+
for _, f := range p.TSProgram.SourceFiles() {
|
|
407
|
+
if f.IsDeclarationFile {
|
|
408
|
+
continue
|
|
409
|
+
}
|
|
410
|
+
out = append(out, f)
|
|
411
|
+
}
|
|
412
|
+
return out
|
|
400
413
|
}
|
|
401
414
|
|
|
402
415
|
// ApplyLinkedPlugins runs registered linked ProgramPlugin hooks exactly once.
|
|
403
416
|
func (p *Program) ApplyLinkedPlugins() error {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
417
|
+
if p == nil || p.pluginsApplied {
|
|
418
|
+
return nil
|
|
419
|
+
}
|
|
420
|
+
p.pluginsApplied = true
|
|
421
|
+
return p.plugins.apply(p)
|
|
409
422
|
}
|
|
410
423
|
|
|
411
424
|
// Diagnostics returns project diagnostics that must block compilation or
|
|
412
425
|
// runtime execution before any JavaScript is emitted or evaluated.
|
|
413
426
|
func (p *Program) Diagnostics() []Diagnostic {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
427
|
+
if p == nil || p.TSProgram == nil {
|
|
428
|
+
return []Diagnostic{{Message: "driver: nil program"}}
|
|
429
|
+
}
|
|
430
|
+
ctx := context.Background()
|
|
431
|
+
raw := shimcompiler.GetDiagnosticsOfAnyProgram(
|
|
432
|
+
ctx,
|
|
433
|
+
p.TSProgram,
|
|
434
|
+
nil,
|
|
435
|
+
false,
|
|
436
|
+
p.TSProgram.GetBindDiagnostics,
|
|
437
|
+
p.TSProgram.GetSemanticDiagnostics,
|
|
438
|
+
)
|
|
439
|
+
raw = filterDiagnostics(raw)
|
|
440
|
+
return convertDiagnostics(shimcompiler.SortAndDeduplicateDiagnostics(raw))
|
|
428
441
|
}
|
|
429
442
|
|
|
443
|
+
// filterDiagnostics removes diagnostics that are false positives in ttsc's
|
|
444
|
+
// compilation model. Currently it suppresses unused type-parameter warnings
|
|
445
|
+
// on overload signatures that have no body (see isUnusedOverloadSignatureTypeParameterDiagnostic).
|
|
430
446
|
func filterDiagnostics(in []*ast.Diagnostic) []*ast.Diagnostic {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
447
|
+
out := in[:0]
|
|
448
|
+
for _, d := range in {
|
|
449
|
+
if isUnusedOverloadSignatureTypeParameterDiagnostic(d) {
|
|
450
|
+
continue
|
|
451
|
+
}
|
|
452
|
+
out = append(out, d)
|
|
453
|
+
}
|
|
454
|
+
return out
|
|
439
455
|
}
|
|
440
456
|
|
|
457
|
+
// isUnusedOverloadSignatureTypeParameterDiagnostic reports true when the
|
|
458
|
+
// diagnostic is TS6196 ("unused declaration") or TS6205 ("all type parameters
|
|
459
|
+
// are unused") on a function declaration that has no body — i.e., an overload
|
|
460
|
+
// signature. tsgo fires these on overloads whose type parameters are used only
|
|
461
|
+
// in the implementation signature, which is a false positive: the overload
|
|
462
|
+
// signatures are required for narrowing and their type parameters are
|
|
463
|
+
// effectively forwarded to the implementation.
|
|
441
464
|
func isUnusedOverloadSignatureTypeParameterDiagnostic(d *ast.Diagnostic) bool {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
465
|
+
if d == nil || d.File() == nil {
|
|
466
|
+
return false
|
|
467
|
+
}
|
|
468
|
+
switch d.Code() {
|
|
469
|
+
case 6196, 6205: // unused declaration / all type parameters are unused
|
|
470
|
+
default:
|
|
471
|
+
return false
|
|
472
|
+
}
|
|
473
|
+
node := ast.GetNodeAtPosition(d.File(), d.Pos(), false)
|
|
474
|
+
for node != nil {
|
|
475
|
+
if node.Kind == ast.KindFunctionDeclaration {
|
|
476
|
+
return node.Body() == nil
|
|
477
|
+
}
|
|
478
|
+
node = node.Parent
|
|
479
|
+
}
|
|
480
|
+
return false
|
|
458
481
|
}
|
|
459
482
|
|
|
460
483
|
// convertDiagnostics translates shim-specific diagnostics into the plain
|
|
461
484
|
// Diagnostic struct with line/column populated via tsgo's ECMALineMap (the
|
|
462
485
|
// same helper tsc uses for its "file:line:col: message" banner).
|
|
463
486
|
func convertDiagnostics(in []*ast.Diagnostic) []Diagnostic {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
487
|
+
out := make([]Diagnostic, 0, len(in))
|
|
488
|
+
for _, d := range in {
|
|
489
|
+
if d == nil {
|
|
490
|
+
continue
|
|
491
|
+
}
|
|
492
|
+
diag := Diagnostic{Code: d.Code(), Message: d.String(), raw: d}
|
|
493
|
+
if file := d.File(); file != nil {
|
|
494
|
+
diag.File = file.FileName()
|
|
495
|
+
if pos := d.Pos(); pos >= 0 {
|
|
496
|
+
length := d.Len()
|
|
497
|
+
diag.Start = &pos
|
|
498
|
+
diag.Length = &length
|
|
499
|
+
line, col := shimscanner.GetECMALineAndByteOffsetOfPosition(file, pos)
|
|
500
|
+
diag.Line = line + 1
|
|
501
|
+
diag.Column = col + 1
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
out = append(out, diag)
|
|
505
|
+
}
|
|
506
|
+
return out
|
|
484
507
|
}
|