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.
- package/LICENSE +1 -1
- package/README.md +216 -302
- package/cmd/ttsc/build.go +128 -0
- package/cmd/ttsc/main.go +222 -0
- package/cmd/ttsc/main_test.go +288 -0
- package/cmd/ttsc/transform.go +166 -0
- package/driver/host.go +31 -0
- package/driver/program.go +298 -0
- package/driver/rewrite.go +315 -0
- package/go.mod +48 -0
- package/go.sum +22 -0
- package/lib/api.d.ts +91 -0
- package/lib/api.js +325 -0
- package/lib/api.js.map +1 -0
- package/lib/cli.d.ts +1 -0
- package/lib/cli.js +481 -0
- package/lib/cli.js.map +1 -0
- package/lib/index.d.ts +17 -0
- package/lib/index.js +34 -0
- package/lib/index.js.map +1 -0
- package/lib/native.d.ts +15 -0
- package/lib/native.js +34 -0
- package/lib/native.js.map +1 -0
- package/lib/platform.d.ts +66 -0
- package/lib/platform.js +170 -0
- package/lib/platform.js.map +1 -0
- package/lib/plugin.d.ts +47 -0
- package/lib/plugin.js +150 -0
- package/lib/plugin.js.map +1 -0
- package/lib/project.d.ts +32 -0
- package/lib/project.js +303 -0
- package/lib/project.js.map +1 -0
- package/package.json +70 -63
- package/shim/ast/extra-shim.json +4 -0
- package/shim/ast/go.mod +5 -0
- package/shim/ast/shim.go +117 -0
- package/shim/bundled/extra-shim.json +1 -0
- package/shim/bundled/go.mod +5 -0
- package/shim/bundled/shim.go +19 -0
- package/shim/checker/extra-shim.json +31 -0
- package/shim/checker/go.mod +5 -0
- package/shim/checker/shim.go +146 -0
- package/shim/compiler/extra-shim.json +4 -0
- package/shim/compiler/go.mod +5 -0
- package/shim/compiler/shim.go +50 -0
- package/shim/core/extra-shim.json +4 -0
- package/shim/core/go.mod +5 -0
- package/shim/core/shim.go +11 -0
- package/shim/diagnosticwriter/go.mod +5 -0
- package/shim/diagnosticwriter/shim.go +29 -0
- package/shim/parser/extra-shim.json +4 -0
- package/shim/parser/go.mod +5 -0
- package/shim/parser/shim.go +55 -0
- package/shim/scanner/extra-shim.json +4 -0
- package/shim/scanner/go.mod +5 -0
- package/shim/scanner/shim.go +96 -0
- package/shim/tsoptions/extra-shim.json +4 -0
- package/shim/tsoptions/go.mod +5 -0
- package/shim/tsoptions/shim.go +123 -0
- package/shim/tspath/extra-shim.json +4 -0
- package/shim/tspath/go.mod +5 -0
- package/shim/tspath/shim.go +165 -0
- package/shim/vfs/cachedvfs/extra-shim.json +4 -0
- package/shim/vfs/cachedvfs/go.mod +5 -0
- package/shim/vfs/cachedvfs/shim.go +12 -0
- package/shim/vfs/extra-shim.json +4 -0
- package/shim/vfs/go.mod +5 -0
- package/shim/vfs/osvfs/extra-shim.json +4 -0
- package/shim/vfs/osvfs/go.mod +5 -0
- package/shim/vfs/osvfs/shim.go +13 -0
- package/shim/vfs/shim.go +19 -0
- package/src/api.ts +416 -0
- package/src/cli.ts +492 -0
- package/src/index.ts +18 -0
- package/src/launcher/ttsc-dev.js +42 -0
- package/src/launcher/ttsc.js +19 -0
- package/src/native.ts +60 -0
- package/src/platform.ts +163 -0
- package/src/plugin.ts +241 -0
- package/src/project.ts +330 -0
- package/test/native.test.ts +60 -0
- package/test/package.test.ts +123 -0
- package/test/platform.test.ts +58 -0
- package/test/project.test.ts +195 -0
- package/tools/gen_shims/go.mod +14 -0
- package/tools/gen_shims/go.sum +12 -0
- package/tools/gen_shims/main.go +446 -0
- package/CHANGELOG.md +0 -2
- package/bin/tsc +0 -2
- package/bin/tsserver +0 -2
- package/lib/PluginCreator.d.ts +0 -67
- package/lib/PluginCreator.js +0 -190
- package/lib/cancelationToken.d.ts +0 -0
- package/lib/cancelationToken.js +0 -2
- package/lib/loadTypescript.d.ts +0 -7
- package/lib/loadTypescript.js +0 -56
- package/lib/patchCreateProgram.d.ts +0 -21
- package/lib/patchCreateProgram.js +0 -184
- package/lib/tsc.d.ts +0 -1
- package/lib/tsc.js +0 -24
- package/lib/tsserver.d.ts +0 -0
- package/lib/tsserver.js +0 -2
- package/lib/tsserverlibrary.d.ts +0 -3
- package/lib/tsserverlibrary.js +0 -4
- package/lib/typescript.d.ts +0 -3
- package/lib/typescript.js +0 -4
- package/lib/typescriptServices.d.ts +0 -3
- package/lib/typescriptServices.js +0 -4
- package/lib/typingsInstaller.d.ts +0 -0
- package/lib/typingsInstaller.js +0 -2
- package/lib/watchGuard.d.ts +0 -0
- 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
|
+
}
|