ttsc 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -6
- package/cmd/ttsc/api_compile.go +101 -106
- package/cmd/ttsc/api_compile_branches_test.go +81 -0
- package/cmd/ttsc/api_transform.go +49 -54
- package/cmd/ttsc/api_transform_branches_test.go +41 -0
- package/cmd/ttsc/build.go +108 -109
- package/cmd/ttsc/build_branches_test.go +130 -0
- package/cmd/ttsc/helpers_test.go +47 -0
- package/cmd/ttsc/main.go +104 -103
- package/cmd/ttsc/main_alias_branches_test.go +29 -0
- package/cmd/ttscserver/main.go +104 -96
- package/cmd/ttscserver/run_lsp_prefers_tsgo_flag_test.go +42 -0
- package/cmd/ttscserver/run_lsp_reports_server_error_test.go +23 -25
- package/cmd/ttscserver/run_lsp_uses_tsgo_environment_test.go +44 -0
- package/cmd/utility-host/main.go +42 -0
- package/driver/lsp.go +60 -0
- package/driver/plugins.go +122 -0
- package/driver/program.go +346 -356
- package/driver/rewrite.go +336 -339
- package/{driver → internal/lspserver}/lsp_envelope.go +70 -64
- package/{driver → internal/lspserver}/lsp_frame.go +62 -62
- package/{driver → internal/lspserver}/lsp_plugin.go +58 -58
- package/internal/lspserver/lsp_proxy.go +391 -0
- package/internal/lspserver/lsp_server.go +231 -0
- package/lib/TtscCompiler.d.ts +3 -3
- package/lib/TtscCompiler.js +20 -6
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/resolveTsgo.d.ts +1 -0
- package/lib/compiler/internal/resolveTsgo.js +15 -4
- package/lib/compiler/internal/resolveTsgo.js.map +1 -1
- package/lib/compiler/internal/runBuild.js +12 -4
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +12 -33
- package/lib/compiler/internal/sharedHostHelpers.js +27 -70
- package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +12 -5
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/lib/index.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +9 -4
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.d.ts +1 -0
- package/lib/launcher/internal/runTtscserver.js +38 -1
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.d.ts +3 -0
- package/lib/plugin/internal/buildSourcePlugin.js +444 -41
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.js +153 -34
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerContext.d.ts +2 -1
- package/lib/structures/ITtscPlugin.d.ts +8 -13
- package/lib/structures/ITtscPluginFactoryContext.d.ts +4 -5
- package/lib/structures/ITtscProjectPluginConfig.d.ts +2 -2
- package/lib/structures/internal/ITtscLoadedNativePlugin.d.ts +4 -2
- package/package.json +8 -8
- package/shim/lsp/shim.go +11 -17
- package/src/TtscCompiler.ts +20 -7
- package/src/compiler/internal/resolveTsgo.ts +18 -5
- package/src/compiler/internal/runBuild.ts +17 -4
- package/src/compiler/internal/sharedHostHelpers.ts +34 -67
- package/src/compiler/internal/transformProjectInMemory.ts +18 -5
- package/src/index.ts +0 -1
- package/src/launcher/internal/runTtsc.ts +9 -4
- package/src/launcher/internal/runTtscserver.ts +43 -1
- package/src/plugin/internal/buildSourcePlugin.ts +475 -41
- package/src/plugin/internal/loadProjectPlugins.ts +199 -39
- package/src/structures/ITtscCompilerContext.ts +2 -1
- package/src/structures/ITtscPlugin.ts +9 -13
- package/src/structures/ITtscPluginFactoryContext.ts +4 -5
- package/src/structures/ITtscProjectPluginConfig.ts +2 -2
- package/src/structures/internal/ITtscLoadedNativePlugin.ts +4 -2
- package/utility/host.go +267 -1296
- package/driver/lsp_proxy.go +0 -394
- package/driver/lsp_server.go +0 -197
- package/lib/defineTtscPlugin.d.ts +0 -18
- package/lib/defineTtscPlugin.js +0 -7
- package/lib/defineTtscPlugin.js.map +0 -1
- package/src/defineTtscPlugin.ts +0 -26
package/driver/program.go
CHANGED
|
@@ -1,23 +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
|
-
|
|
19
|
-
"github.com/microsoft/typescript-go/shim/tspath"
|
|
20
|
-
"github.com/microsoft/typescript-go/shim/vfs"
|
|
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"
|
|
21
19
|
)
|
|
22
20
|
|
|
23
21
|
// Diagnostic is the compilation diagnostic shape ttsc passes around. Kept
|
|
@@ -29,16 +27,16 @@ import (
|
|
|
29
27
|
// `@ttsc/lint`). At most one of `raw` / `lint` is non-nil; both nil falls
|
|
30
28
|
// back to the legacy single-line form.
|
|
31
29
|
type Diagnostic struct {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
// Severity classifies a diagnostic's blast radius. ttsc treats Error as a
|
|
@@ -46,12 +44,12 @@ type Diagnostic struct {
|
|
|
46
44
|
type Severity int
|
|
47
45
|
|
|
48
46
|
const (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
55
53
|
)
|
|
56
54
|
|
|
57
55
|
// IsError reports whether the diagnostic counts toward the build's error
|
|
@@ -65,60 +63,60 @@ func (d Diagnostic) IsError() bool { return d.Severity == SeverityError }
|
|
|
65
63
|
// rule's enum index). Severity controls both the rendered banner color and
|
|
66
64
|
// the exit-code outcome.
|
|
67
65
|
func NewLintDiagnostic(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
file *ast.SourceFile,
|
|
67
|
+
pos, end int,
|
|
68
|
+
code int32,
|
|
69
|
+
severity Severity,
|
|
70
|
+
message string,
|
|
73
71
|
) Diagnostic {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
97
95
|
}
|
|
98
96
|
|
|
99
97
|
// SourceFile returns the program source file matching filename.
|
|
100
98
|
func (p *Program) SourceFile(filename string) *ast.SourceFile {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
111
109
|
}
|
|
112
110
|
|
|
113
111
|
// String returns a `path:line:col: message` formatted string.
|
|
114
112
|
func (d Diagnostic) String() string {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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)
|
|
122
120
|
}
|
|
123
121
|
|
|
124
122
|
// WritePrettyDiagnostics renders diagnostics with TypeScript-style colors,
|
|
@@ -127,91 +125,94 @@ func (d Diagnostic) String() string {
|
|
|
127
125
|
// are rendered through the same color/context pipeline; entries without
|
|
128
126
|
// either anchor fall back to the legacy `path:line:col: message` form.
|
|
129
127
|
func WritePrettyDiagnostics(w io.Writer, diagnostics []Diagnostic, cwd string) {
|
|
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
|
-
|
|
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
|
+
}
|
|
158
156
|
}
|
|
159
157
|
|
|
160
158
|
// CountErrors returns the number of diagnostics that should fail the build.
|
|
161
159
|
// tsgo diagnostics carry their own `Error` category; lint diagnostics carry a
|
|
162
160
|
// caller-set Severity. Anything that isn't an explicit warning counts.
|
|
163
161
|
func CountErrors(diagnostics []Diagnostic) int {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
|
186
184
|
}
|
|
187
185
|
|
|
188
186
|
// Program is the shim-agnostic facade the rest of the engine sees.
|
|
189
187
|
type Program struct {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
|
196
197
|
}
|
|
197
198
|
|
|
198
199
|
// LoadProgramOptions controls tsconfig overrides applied before tsgo creates
|
|
199
200
|
// the program. `ForceEmit` is used by `ttsc --emit` and runtime compilation
|
|
200
201
|
// so execution still works when the project defaults to `noEmit`.
|
|
201
202
|
type LoadProgramOptions struct {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
ForceEmit bool
|
|
204
|
+
ForceNoEmit bool
|
|
205
|
+
OutDir string
|
|
206
|
+
SourcePreamble string
|
|
206
207
|
}
|
|
207
208
|
|
|
208
209
|
// Close releases the checker pool lease acquired by LoadProgram.
|
|
209
210
|
func (p *Program) Close() error {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
211
|
+
if p.checkerRelease != nil {
|
|
212
|
+
p.checkerRelease()
|
|
213
|
+
p.checkerRelease = nil
|
|
214
|
+
}
|
|
215
|
+
return nil
|
|
215
216
|
}
|
|
216
217
|
|
|
217
218
|
// ParseTSConfig parses a tsconfig.json file via tsgo's native JSONC parser.
|
|
@@ -221,39 +222,31 @@ func (p *Program) Close() error {
|
|
|
221
222
|
// tsgo's filesystem APIs require absolute paths — mirrors what tsc does when
|
|
222
223
|
// you pass a relative `--project` flag.
|
|
223
224
|
func ParseTSConfig(fs vfs.FS, cwd, tsconfigPath string, host shimcompiler.CompilerHost) (*tsoptions.ParsedCommandLine, []Diagnostic, error) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
if len(parsed.Errors) > 0 {
|
|
236
|
-
return nil, convertDiagnostics(parsed.Errors), nil
|
|
237
|
-
}
|
|
238
|
-
return parsed, nil, nil
|
|
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
|
|
239
235
|
}
|
|
240
236
|
|
|
241
237
|
// CreateProgramFromConfig builds a tsgo Program from the parsed config.
|
|
242
238
|
func CreateProgramFromConfig(parsed *tsoptions.ParsedCommandLine, host shimcompiler.CompilerHost) (*shimcompiler.Program, []Diagnostic, error) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
return nil, nil, errors.New("shimcompiler.NewProgram returned nil")
|
|
255
|
-
}
|
|
256
|
-
return p, nil, nil
|
|
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
|
|
257
250
|
}
|
|
258
251
|
|
|
259
252
|
// LoadProgram is the one-shot convenience used by `ttsc`.
|
|
@@ -262,233 +255,230 @@ func CreateProgramFromConfig(parsed *tsoptions.ParsedCommandLine, host shimcompi
|
|
|
262
255
|
//
|
|
263
256
|
// cwd must be absolute; tsconfigPath may be relative to cwd.
|
|
264
257
|
func LoadProgram(cwd, tsconfigPath string, options LoadProgramOptions) (*Program, []Diagnostic, error) {
|
|
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
|
-
|
|
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
|
|
314
315
|
}
|
|
315
316
|
|
|
316
317
|
func forceEmit(parsed *tsoptions.ParsedCommandLine) {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
options := parsed.ParsedConfig.CompilerOptions
|
|
321
|
-
options.NoEmit = core.TSFalse
|
|
322
|
-
options.EmitDeclarationOnly = core.TSFalse
|
|
318
|
+
options := parsed.ParsedConfig.CompilerOptions
|
|
319
|
+
options.NoEmit = core.TSFalse
|
|
320
|
+
options.EmitDeclarationOnly = core.TSFalse
|
|
323
321
|
}
|
|
324
322
|
|
|
325
323
|
func forceNoEmit(parsed *tsoptions.ParsedCommandLine) {
|
|
326
|
-
|
|
327
|
-
return
|
|
328
|
-
}
|
|
329
|
-
parsed.ParsedConfig.CompilerOptions.NoEmit = core.TSTrue
|
|
324
|
+
parsed.ParsedConfig.CompilerOptions.NoEmit = core.TSTrue
|
|
330
325
|
}
|
|
331
326
|
|
|
332
327
|
func overrideOutDir(cwd string, parsed *tsoptions.ParsedCommandLine, outDir string) {
|
|
333
|
-
|
|
334
|
-
return
|
|
335
|
-
}
|
|
336
|
-
parsed.ParsedConfig.CompilerOptions.OutDir = tspath.ResolvePath(cwd, outDir)
|
|
328
|
+
parsed.ParsedConfig.CompilerOptions.OutDir = tspath.ResolvePath(cwd, outDir)
|
|
337
329
|
}
|
|
338
330
|
|
|
339
331
|
type sourcePreambleFS struct {
|
|
340
|
-
|
|
341
|
-
|
|
332
|
+
vfs.FS
|
|
333
|
+
preamble string
|
|
342
334
|
}
|
|
343
335
|
|
|
344
336
|
func (fs sourcePreambleFS) ReadFile(filePath string) (string, bool) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
func (fs sourcePreambleFS) WriteFile(filePath string, data string) error {
|
|
353
|
-
return fs.FS.WriteFile(filePath, data)
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
func (fs sourcePreambleFS) AppendFile(filePath string, data string) error {
|
|
357
|
-
return fs.FS.AppendFile(filePath, data)
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
func (fs sourcePreambleFS) Remove(filePath string) error {
|
|
361
|
-
return fs.FS.Remove(filePath)
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
func (fs sourcePreambleFS) Chtimes(filePath string, aTime time.Time, mTime time.Time) error {
|
|
365
|
-
return fs.FS.Chtimes(filePath, aTime, mTime)
|
|
337
|
+
contents, ok := fs.FS.ReadFile(filePath)
|
|
338
|
+
if !ok || !isSourcePreambleTarget(filePath) {
|
|
339
|
+
return contents, ok
|
|
340
|
+
}
|
|
341
|
+
return ApplySourcePreamble(contents, fs.preamble), true
|
|
366
342
|
}
|
|
367
343
|
|
|
368
344
|
func isSourcePreambleTarget(filePath string) bool {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
345
|
+
lower := strings.ToLower(filepath.ToSlash(filePath))
|
|
346
|
+
for _, suffix := range []string{".d.ts", ".d.mts", ".d.cts"} {
|
|
347
|
+
if strings.HasSuffix(lower, suffix) {
|
|
348
|
+
return false
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
for _, suffix := range []string{".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"} {
|
|
352
|
+
if strings.HasSuffix(lower, suffix) {
|
|
353
|
+
return true
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return false
|
|
381
357
|
}
|
|
382
358
|
|
|
383
359
|
// ApplySourcePreamble inserts a generated source preamble without moving the
|
|
384
360
|
// file's BOM or hashbang away from the first bytes of the physical output.
|
|
385
361
|
func ApplySourcePreamble(text string, preamble string) string {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
362
|
+
if preamble == "" {
|
|
363
|
+
return text
|
|
364
|
+
}
|
|
365
|
+
bom := ""
|
|
366
|
+
rest := text
|
|
367
|
+
if strings.HasPrefix(rest, "\ufeff") {
|
|
368
|
+
bom = "\ufeff"
|
|
369
|
+
rest = strings.TrimPrefix(rest, "\ufeff")
|
|
370
|
+
}
|
|
371
|
+
if strings.HasPrefix(rest, "#!") {
|
|
372
|
+
end := strings.IndexByte(rest, '\n')
|
|
373
|
+
if end < 0 {
|
|
374
|
+
return bom + rest + "\n" + preamble
|
|
375
|
+
}
|
|
376
|
+
return bom + rest[:end+1] + preamble + rest[end+1:]
|
|
377
|
+
}
|
|
378
|
+
return bom + preamble + rest
|
|
403
379
|
}
|
|
404
380
|
|
|
405
381
|
// SourceFiles exposes the program's user-authored source files (declaration
|
|
406
382
|
// files filtered out).
|
|
407
383
|
func (p *Program) SourceFiles() []*ast.SourceFile {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
384
|
+
_ = p.ApplyLinkedPlugins()
|
|
385
|
+
return p.sourceFilesRaw()
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
func (p *Program) sourceFilesRaw() []*ast.SourceFile {
|
|
389
|
+
out := make([]*ast.SourceFile, 0)
|
|
390
|
+
if p == nil || p.TSProgram == nil {
|
|
391
|
+
return out
|
|
392
|
+
}
|
|
393
|
+
for _, f := range p.TSProgram.SourceFiles() {
|
|
394
|
+
if f.IsDeclarationFile {
|
|
395
|
+
continue
|
|
396
|
+
}
|
|
397
|
+
out = append(out, f)
|
|
398
|
+
}
|
|
399
|
+
return out
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ApplyLinkedPlugins runs registered linked ProgramPlugin hooks exactly once.
|
|
403
|
+
func (p *Program) ApplyLinkedPlugins() error {
|
|
404
|
+
if p == nil || p.pluginsApplied {
|
|
405
|
+
return nil
|
|
406
|
+
}
|
|
407
|
+
p.pluginsApplied = true
|
|
408
|
+
return p.plugins.apply(p)
|
|
419
409
|
}
|
|
420
410
|
|
|
421
411
|
// Diagnostics returns project diagnostics that must block compilation or
|
|
422
412
|
// runtime execution before any JavaScript is emitted or evaluated.
|
|
423
413
|
func (p *Program) Diagnostics() []Diagnostic {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
414
|
+
if p == nil || p.TSProgram == nil {
|
|
415
|
+
return []Diagnostic{{Message: "driver: nil program"}}
|
|
416
|
+
}
|
|
417
|
+
ctx := context.Background()
|
|
418
|
+
raw := shimcompiler.GetDiagnosticsOfAnyProgram(
|
|
419
|
+
ctx,
|
|
420
|
+
p.TSProgram,
|
|
421
|
+
nil,
|
|
422
|
+
false,
|
|
423
|
+
p.TSProgram.GetBindDiagnostics,
|
|
424
|
+
p.TSProgram.GetSemanticDiagnostics,
|
|
425
|
+
)
|
|
426
|
+
raw = filterDiagnostics(raw)
|
|
427
|
+
return convertDiagnostics(shimcompiler.SortAndDeduplicateDiagnostics(raw))
|
|
438
428
|
}
|
|
439
429
|
|
|
440
430
|
func filterDiagnostics(in []*ast.Diagnostic) []*ast.Diagnostic {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
431
|
+
out := in[:0]
|
|
432
|
+
for _, d := range in {
|
|
433
|
+
if isUnusedOverloadSignatureTypeParameterDiagnostic(d) {
|
|
434
|
+
continue
|
|
435
|
+
}
|
|
436
|
+
out = append(out, d)
|
|
437
|
+
}
|
|
438
|
+
return out
|
|
449
439
|
}
|
|
450
440
|
|
|
451
441
|
func isUnusedOverloadSignatureTypeParameterDiagnostic(d *ast.Diagnostic) bool {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
442
|
+
if d == nil || d.File() == nil {
|
|
443
|
+
return false
|
|
444
|
+
}
|
|
445
|
+
switch d.Code() {
|
|
446
|
+
case 6196, 6205: // unused declaration / all type parameters are unused
|
|
447
|
+
default:
|
|
448
|
+
return false
|
|
449
|
+
}
|
|
450
|
+
node := ast.GetNodeAtPosition(d.File(), d.Pos(), false)
|
|
451
|
+
for node != nil {
|
|
452
|
+
if node.Kind == ast.KindFunctionDeclaration {
|
|
453
|
+
return node.Body() == nil
|
|
454
|
+
}
|
|
455
|
+
node = node.Parent
|
|
456
|
+
}
|
|
457
|
+
return false
|
|
468
458
|
}
|
|
469
459
|
|
|
470
460
|
// convertDiagnostics translates shim-specific diagnostics into the plain
|
|
471
461
|
// Diagnostic struct with line/column populated via tsgo's ECMALineMap (the
|
|
472
462
|
// same helper tsc uses for its "file:line:col: message" banner).
|
|
473
463
|
func convertDiagnostics(in []*ast.Diagnostic) []Diagnostic {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
464
|
+
out := make([]Diagnostic, 0, len(in))
|
|
465
|
+
for _, d := range in {
|
|
466
|
+
if d == nil {
|
|
467
|
+
continue
|
|
468
|
+
}
|
|
469
|
+
diag := Diagnostic{Code: d.Code(), Message: d.String(), raw: d}
|
|
470
|
+
if file := d.File(); file != nil {
|
|
471
|
+
diag.File = file.FileName()
|
|
472
|
+
if pos := d.Pos(); pos >= 0 {
|
|
473
|
+
length := d.Len()
|
|
474
|
+
diag.Start = &pos
|
|
475
|
+
diag.Length = &length
|
|
476
|
+
line, col := shimscanner.GetECMALineAndByteOffsetOfPosition(file, pos)
|
|
477
|
+
diag.Line = line + 1
|
|
478
|
+
diag.Column = col + 1
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
out = append(out, diag)
|
|
482
|
+
}
|
|
483
|
+
return out
|
|
494
484
|
}
|