Skip to content

execute command line initial implementation #241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2fd091c
execute command line
iisaduan Jan 21, 2025
692c3bb
enable emit
iisaduan Jan 21, 2025
b26dc18
Merge branch 'main' of https://github.com/microsoft/typescript-go int…
iisaduan Jan 22, 2025
175aebf
move sys to main.go
iisaduan Jan 23, 2025
eac308b
Merge branch 'main' of https://github.com/microsoft/typescript-go int…
iisaduan Jan 24, 2025
2141369
add tsconfigparsing
iisaduan Jan 24, 2025
c31d18d
add file baselining
iisaduan Jan 27, 2025
c258b28
Merge branch 'main' of https://github.com/microsoft/typescript-go int…
iisaduan Jan 27, 2025
3984ee0
move tests into execute_test
iisaduan Jan 28, 2025
3003b5c
move `os.exit` into `main` only
iisaduan Jan 28, 2025
a8924da
clean up, check tsconfig parsing
iisaduan Jan 29, 2025
609e180
Write() -> Writer() and returns a io.writer
iisaduan Jan 29, 2025
42fe8e4
remove sys.exit()
iisaduan Jan 29, 2025
bb688b5
converttooptionswithabsolutepaths
iisaduan Jan 29, 2025
4b0a00e
Merge branch 'main' of https://github.com/microsoft/typescript-go int…
iisaduan Jan 29, 2025
62fd055
more code clean up
iisaduan Jan 29, 2025
399b4d7
refactor `host` to be created much later (and other clean ups)
iisaduan Jan 30, 2025
0b89c7a
remove diagnosticreporter
iisaduan Jan 30, 2025
2b99f18
unexport sys things
iisaduan Jan 30, 2025
098e82d
address comments, fix tests
iisaduan Jan 31, 2025
8045b91
refactor how `tsc` arg is checked in `main.go`
iisaduan Jan 31, 2025
4d12fb7
Merge branch 'main' of https://github.com/microsoft/typescript-go int…
iisaduan Jan 31, 2025
085905a
refactor format opts
iisaduan Feb 3, 2025
8a45430
refactor pretty
iisaduan Feb 3, 2025
227e3a0
move testing function
iisaduan Feb 3, 2025
0560997
newline -> newLine
iisaduan Feb 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions cmd/tsgo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/microsoft/typescript-go/internal/compiler/diagnostics"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/diagnosticwriter"
"github.com/microsoft/typescript-go/internal/execute"
"github.com/microsoft/typescript-go/internal/scanner"
"github.com/microsoft/typescript-go/internal/tspath"
"github.com/microsoft/typescript-go/internal/vfs"
Expand Down Expand Up @@ -113,6 +114,10 @@ func parseArgs() *cliOptions {
}

func main() {
if args := os.Args[1:]; len(args) > 0 && args[0] == "tsc" {
exitCode := execute.CommandLine(newSystem(), nil, args[1:])
os.Exit(int(exitCode))
}
opts := parseArgs()

if opts.devel.pprofDir != "" {
Expand Down Expand Up @@ -177,9 +182,8 @@ func main() {
var bindTime, checkTime time.Duration

diagnostics := program.GetOptionsDiagnostics()
formatOpts := getFormatOpts(host)
if len(diagnostics) != 0 {
printDiagnostics(diagnostics, formatOpts, compilerOptions)
printDiagnostics(diagnostics, host, compilerOptions)
os.Exit(1)
}

Expand Down Expand Up @@ -217,7 +221,7 @@ func main() {
runtime.ReadMemStats(&memStats)

if !opts.devel.quiet && len(diagnostics) != 0 {
printDiagnostics(diagnostics, formatOpts, compilerOptions)
printDiagnostics(diagnostics, host, compilerOptions)
}

if compilerOptions.ListFiles.IsTrue() {
Expand Down Expand Up @@ -293,7 +297,8 @@ func getFormatOpts(host ts.CompilerHost) *diagnosticwriter.FormattingOptions {
}
}

func printDiagnostics(diagnostics []*ast.Diagnostic, formatOpts *diagnosticwriter.FormattingOptions, compilerOptions *core.CompilerOptions) {
func printDiagnostics(diagnostics []*ast.Diagnostic, host ts.CompilerHost, compilerOptions *core.CompilerOptions) {
formatOpts := getFormatOpts(host)
if compilerOptions.Pretty.IsTrueOrUnknown() {
diagnosticwriter.FormatDiagnosticsWithColorAndContext(os.Stdout, diagnostics, formatOpts)
fmt.Fprintln(os.Stdout)
Expand Down
56 changes: 56 additions & 0 deletions cmd/tsgo/sys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import (
"fmt"
"io"
"os"
"runtime"

"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/execute"
"github.com/microsoft/typescript-go/internal/tspath"
"github.com/microsoft/typescript-go/internal/vfs"
)

type osSys struct {
writer io.Writer
fs vfs.FS
newLine string
cwd string
}

func (s *osSys) FS() vfs.FS {
return s.fs
}

func (s *osSys) GetCurrentDirectory() string {
return s.cwd
}

func (s *osSys) NewLine() string {
return s.newLine
}

func (s *osSys) Writer() io.Writer {
return s.writer
}

func (s *osSys) EndWrite() {
// do nothing, this is needed in the interface for testing
// todo: revisit if improving tsc/build/watch unittest baselines
}

func newSystem() *osSys {
cwd, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting current directory: %v\n", err)
os.Exit(int(execute.ExitStatusInvalidProject_OutputsSkipped))
}

return &osSys{
cwd: tspath.NormalizePath(cwd),
fs: vfs.FromOS(),
writer: os.Stdout,
newLine: core.IfElse(runtime.GOOS == "windows", "\r\n", "\n"),
}
}
4 changes: 2 additions & 2 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ type WideningContext struct {

type Program interface {
Options() *core.CompilerOptions
Files() []*ast.SourceFile
SourceFiles() []*ast.SourceFile

BindSourceFiles()
GetEmitModuleFormatOfFile(sourceFile *ast.SourceFile) core.ModuleKind
Expand Down Expand Up @@ -779,7 +779,7 @@ func NewChecker(program Program) *Checker {
c.program = program
// c.host = program.host
c.compilerOptions = program.Options()
c.files = program.Files()
c.files = program.SourceFiles()
c.fileIndexMap = createFileIndexMap(c.files)
c.compareSymbols = c.compareSymbolsWorker // Closure optimization
c.languageVersion = c.compilerOptions.GetEmitScriptTarget()
Expand Down
4 changes: 4 additions & 0 deletions internal/compiler/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ func (h *compilerHost) FS() vfs.FS {
return h.fs
}

func (h *compilerHost) SetOptions(options *core.CompilerOptions) {
h.options = options
}

func (h *compilerHost) GetCurrentDirectory() string {
return h.currentDirectory
}
Expand Down
65 changes: 42 additions & 23 deletions internal/compiler/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/binder"
"github.com/microsoft/typescript-go/internal/bundled"
"github.com/microsoft/typescript-go/internal/checker"
"github.com/microsoft/typescript-go/internal/compiler/diagnostics"
"github.com/microsoft/typescript-go/internal/compiler/module"
Expand All @@ -27,26 +28,26 @@ type ProgramOptions struct {
SingleThreaded bool
ProjectReference []core.ProjectReference
DefaultLibraryPath string
OptionsDiagnostics []*ast.Diagnostic
}

type Program struct {
host CompilerHost
programOptions ProgramOptions
compilerOptions *core.CompilerOptions
configFilePath string
nodeModules map[string]*ast.SourceFile
checkers []*checker.Checker
checkersByFile map[*ast.SourceFile]*checker.Checker
currentDirectory string
host CompilerHost
programOptions ProgramOptions
compilerOptions *core.CompilerOptions
configFilePath string
nodeModules map[string]*ast.SourceFile
checkers []*checker.Checker
checkersByFile map[*ast.SourceFile]*checker.Checker
currentDirectory string
optionsDiagnostics []*ast.Diagnostic

resolver *module.Resolver
resolvedModules map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule]

comparePathsOptions tspath.ComparePathsOptions
defaultLibraryPath string

optionsDiagnostics []*ast.Diagnostic

files []*ast.SourceFile
filesByPath map[tspath.Path]*ast.SourceFile

Expand All @@ -72,6 +73,7 @@ func NewProgram(options ProgramOptions) *Program {
p := &Program{}
p.programOptions = options
p.compilerOptions = options.Options
p.optionsDiagnostics = options.OptionsDiagnostics
if p.compilerOptions == nil {
p.compilerOptions = &core.CompilerOptions{}
}
Expand Down Expand Up @@ -163,17 +165,22 @@ func NewProgram(options ProgramOptions) *Program {
return p
}

func (p *Program) Files() []*ast.SourceFile {
return p.files
func NewProgramFromParsedCommandLine(config *tsoptions.ParsedCommandLine, host CompilerHost) *Program {
programOptions := ProgramOptions{
RootFiles: config.FileNames(),
Options: config.CompilerOptions(),
Host: host,
// todo: ProjectReferences
OptionsDiagnostics: config.GetConfigFileParsingDiagnostics(),
DefaultLibraryPath: bundled.LibPath(),
}
return NewProgram(programOptions)
}

func (p *Program) SourceFiles() []*ast.SourceFile { return p.files }
func (p *Program) Options() *core.CompilerOptions { return p.compilerOptions }
func (p *Program) Host() CompilerHost { return p.host }

func (p *Program) GetOptionsDiagnostics() []*ast.Diagnostic {
return p.optionsDiagnostics
}
func (p *Program) SourceFiles() []*ast.SourceFile { return p.files }
func (p *Program) Options() *core.CompilerOptions { return p.compilerOptions }
func (p *Program) Host() CompilerHost { return p.host }
func (p *Program) OptionsDiagnostics() []*ast.Diagnostic { return p.optionsDiagnostics }

func (p *Program) BindSourceFiles() {
wg := core.NewWorkGroup(p.programOptions.SingleThreaded)
Expand Down Expand Up @@ -274,7 +281,19 @@ func (p *Program) GetGlobalDiagnostics() []*ast.Diagnostic {
for _, checker := range p.checkers {
globalDiagnostics = append(globalDiagnostics, checker.GetGlobalDiagnostics()...)
}
return sortAndDeduplicateDiagnostics(globalDiagnostics)
return SortAndDeduplicateDiagnostics(globalDiagnostics)
}

func (p *Program) GetOptionsDiagnostics() []*ast.Diagnostic {
return SortAndDeduplicateDiagnostics(append(p.GetGlobalDiagnostics(), p.getOptionsDiagnosticsOfConfigFile()...))
}

func (p *Program) getOptionsDiagnosticsOfConfigFile() []*ast.Diagnostic {
// todo update p.configParsingDiagnostics when updateAndGetProgramDiagnostics is implemented
if p.Options() == nil || p.Options().ConfigFilePath == "" {
return nil
}
return p.optionsDiagnostics
}

func (p *Program) getSyntaticDiagnosticsForFile(sourceFile *ast.SourceFile) []*ast.Diagnostic {
Expand Down Expand Up @@ -337,7 +356,7 @@ func isCommentOrBlankLine(text string, pos int) bool {
pos+1 < len(text) && text[pos] == '/' && text[pos+1] == '/'
}

func sortAndDeduplicateDiagnostics(diagnostics []*ast.Diagnostic) []*ast.Diagnostic {
func SortAndDeduplicateDiagnostics(diagnostics []*ast.Diagnostic) []*ast.Diagnostic {
result := slices.Clone(diagnostics)
slices.SortFunc(result, ast.CompareDiagnostics)
return slices.CompactFunc(result, ast.EqualDiagnostics)
Expand All @@ -348,7 +367,7 @@ func (p *Program) getDiagnosticsHelper(sourceFile *ast.SourceFile, ensureBound b
if ensureBound {
binder.BindSourceFile(sourceFile, p.compilerOptions)
}
return sortAndDeduplicateDiagnostics(getDiagnostics(sourceFile))
return SortAndDeduplicateDiagnostics(getDiagnostics(sourceFile))
}
if ensureBound {
p.BindSourceFiles()
Expand All @@ -360,7 +379,7 @@ func (p *Program) getDiagnosticsHelper(sourceFile *ast.SourceFile, ensureBound b
for _, file := range p.files {
result = append(result, getDiagnostics(file)...)
}
return sortAndDeduplicateDiagnostics(result)
return SortAndDeduplicateDiagnostics(result)
}

func (p *Program) TypeCount() int {
Expand Down
16 changes: 16 additions & 0 deletions internal/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,22 @@ func Filter[T any](slice []T, f func(T) bool) []T {
return slice
}

func FilterIndex[T any](slice []T, f func(T, int, []T) bool) []T {
for i, value := range slice {
if !f(value, i, slice) {
result := slices.Clone(slice[:i])
for i++; i < len(slice); i++ {
value = slice[i]
if f(value, i, slice) {
result = append(result, value)
}
}
return result
}
}
return slice
}

func Map[T, U any](slice []T, f func(T) U) []U {
if len(slice) == 0 {
return nil
Expand Down
16 changes: 14 additions & 2 deletions internal/diagnosticwriter/diagnosticwriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ func writeCodeSnippet(writer io.Writer, sourceFile *ast.SourceFile, start int, l
}
}

func FlattenDiagnosticMessage(d *ast.Diagnostic, newLine string) string {
var output strings.Builder
WriteFlattenedDiagnosticMessage(&output, d, newLine)
return output.String()
}

func WriteFlattenedDiagnosticMessage(writer io.Writer, diagnostic *ast.Diagnostic, newline string) {
fmt.Fprint(writer, diagnostic.Message())

Expand Down Expand Up @@ -232,14 +238,17 @@ func WriteErrorSummaryText(output io.Writer, allDiagnostics []*ast.Diagnostic, f
return
}

firstFile := errorSummary.SortedFileList[0]
firstFile := &ast.SourceFile{}
if len(errorSummary.SortedFileList) > 0 {
firstFile = errorSummary.SortedFileList[0]
}
firstFileName := prettyPathForFileError(firstFile, errorSummary.ErrorsByFiles[firstFile], formatOpts)
numErroringFiles := len(errorSummary.ErrorsByFiles)

var message string
if totalErrorCount == 1 {
// Special-case a single error.
if len(errorSummary.GlobalErrors) > 0 {
if len(errorSummary.GlobalErrors) > 0 || firstFileName == "" {
message = diagnostics.Found_1_error.Format()
} else {
message = diagnostics.Found_1_error_in_0.Format(firstFileName)
Expand Down Expand Up @@ -333,6 +342,9 @@ func writeTabularErrorsDisplay(output io.Writer, errorSummary *ErrorSummary, for
}

func prettyPathForFileError(file *ast.SourceFile, fileErrors []*ast.Diagnostic, formatOpts *FormattingOptions) string {
if file == nil || len(fileErrors) == 0 {
return ""
}
line, _ := scanner.GetLineAndCharacterOfPosition(file, fileErrors[0].Loc().Pos())
fileName := file.FileName()
if tspath.PathIsAbsolute(fileName) && tspath.PathIsAbsolute(formatOpts.CurrentDirectory) {
Expand Down
8 changes: 8 additions & 0 deletions internal/execute/export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package execute

import "github.com/microsoft/typescript-go/internal/tsoptions"

func CommandLineTest(sys System, cb cbType, commandLineArgs []string) (*tsoptions.ParsedCommandLine, ExitStatus) {
parsedCommandLine := tsoptions.ParseCommandLine(commandLineArgs, sys)
return parsedCommandLine, executeCommandLineWorker(sys, cb, parsedCommandLine)
}
Loading
Loading