diff --git a/cmd/tsgo/main.go b/cmd/tsgo/main.go index 6946001bca..62a3625e5d 100644 --- a/cmd/tsgo/main.go +++ b/cmd/tsgo/main.go @@ -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" @@ -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 != "" { @@ -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) } @@ -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() { @@ -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) diff --git a/cmd/tsgo/sys.go b/cmd/tsgo/sys.go new file mode 100644 index 0000000000..af6926cd40 --- /dev/null +++ b/cmd/tsgo/sys.go @@ -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"), + } +} diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 814882457a..6c9d9e1e9c 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -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 @@ -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() diff --git a/internal/compiler/host.go b/internal/compiler/host.go index 20e645aaa9..9c72491214 100644 --- a/internal/compiler/host.go +++ b/internal/compiler/host.go @@ -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 } diff --git a/internal/compiler/program.go b/internal/compiler/program.go index 394f224ba8..23b21c67d4 100644 --- a/internal/compiler/program.go +++ b/internal/compiler/program.go @@ -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" @@ -27,17 +28,19 @@ 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] @@ -45,8 +48,6 @@ type Program struct { comparePathsOptions tspath.ComparePathsOptions defaultLibraryPath string - optionsDiagnostics []*ast.Diagnostic - files []*ast.SourceFile filesByPath map[tspath.Path]*ast.SourceFile @@ -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{} } @@ -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) @@ -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 { @@ -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) @@ -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() @@ -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 { diff --git a/internal/core/core.go b/internal/core/core.go index fbf75d3f2f..75342d6741 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -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 diff --git a/internal/diagnosticwriter/diagnosticwriter.go b/internal/diagnosticwriter/diagnosticwriter.go index 5506bddfd4..850d6f0a9e 100644 --- a/internal/diagnosticwriter/diagnosticwriter.go +++ b/internal/diagnosticwriter/diagnosticwriter.go @@ -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()) @@ -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) @@ -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) { diff --git a/internal/execute/export_test.go b/internal/execute/export_test.go new file mode 100644 index 0000000000..2e49e4b825 --- /dev/null +++ b/internal/execute/export_test.go @@ -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) +} diff --git a/internal/execute/outputs.go b/internal/execute/outputs.go new file mode 100644 index 0000000000..9dabe0baab --- /dev/null +++ b/internal/execute/outputs.go @@ -0,0 +1,81 @@ +package execute + +import ( + "fmt" + "strings" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/compiler" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/diagnosticwriter" + "github.com/microsoft/typescript-go/internal/tspath" +) + +func getFormatOptsOfSys(sys System) *diagnosticwriter.FormattingOptions { + return &diagnosticwriter.FormattingOptions{ + NewLine: "\n", + ComparePathsOptions: tspath.ComparePathsOptions{ + CurrentDirectory: sys.GetCurrentDirectory(), + UseCaseSensitiveFileNames: sys.FS().UseCaseSensitiveFileNames(), + }, + } +} + +type diagnosticReporter = func(*ast.Diagnostic) + +func createDiagnosticReporter(sys System, pretty core.Tristate) diagnosticReporter { + formatOpts := getFormatOptsOfSys(sys) + if pretty.IsFalseOrUnknown() { + return func(diagnostic *ast.Diagnostic) { + diagnosticwriter.WriteFormatDiagnostic(sys.Writer(), diagnostic, formatOpts) + sys.EndWrite() + } + } + return func(diagnostic *ast.Diagnostic) { + diagnosticwriter.FormatDiagnosticsWithColorAndContext(sys.Writer(), []*ast.Diagnostic{diagnostic}, formatOpts) + sys.EndWrite() + } +} + +func shouldBePretty(sys System, options *core.CompilerOptions) bool { + if options == nil || options.Pretty.IsTrueOrUnknown() { + // todo: return defaultIsPretty(sys); + return true + } + return options.Pretty.IsTrue() +} + +func createReportErrorSummary(sys System, options *core.CompilerOptions) func(diagnostics []*ast.Diagnostic) { + if shouldBePretty(sys, options) { + formatOpts := getFormatOptsOfSys(sys) + return func(diagnostics []*ast.Diagnostic) { + diagnosticwriter.WriteErrorSummaryText(sys.Writer(), diagnostics, formatOpts) + sys.EndWrite() + } + } + return func(diagnostics []*ast.Diagnostic) {} +} + +func reportStatistics(sys System, program *compiler.Program) { + // todo + stats := []statistic{ + newStatistic("Files", len(program.SourceFiles())), + // newStatistic("Identifiers", program.IdentifierCount()), + // newStatistic("Symbols", program.getSymbolCount()), + newStatistic("Types", program.TypeCount()), + // newStatistic("Instantiations", program.getInstantiationCount()), + } + + for _, stat := range stats { + fmt.Fprintf(sys.Writer(), "%s:"+strings.Repeat(" ", 20-len(stat.name))+"%v\n", stat.name, stat.value) + } +} + +type statistic struct { + name string + value int +} + +func newStatistic(name string, count int) statistic { + return statistic{name, count} +} diff --git a/internal/execute/system.go b/internal/execute/system.go new file mode 100644 index 0000000000..cad2ff06d3 --- /dev/null +++ b/internal/execute/system.go @@ -0,0 +1,28 @@ +package execute + +import ( + "io" + + "github.com/microsoft/typescript-go/internal/vfs" +) + +type System interface { + Writer() io.Writer + EndWrite() // needed for testing + FS() vfs.FS + GetCurrentDirectory() string + NewLine() string // #241 eventually we want to use "\n" +} + +type ExitStatus int + +const ( + ExitStatusSuccess ExitStatus = 0 + ExitStatusDiagnosticsPresent_OutputsSkipped ExitStatus = 1 + ExitStatusDiagnosticsPresent_OutputsGenerated ExitStatus = 2 + ExitStatusInvalidProject_OutputsSkipped ExitStatus = 3 + ExitStatusProjectReferenceCycle_OutputsSkipped ExitStatus = 4 + ExitStatusNotImplemented ExitStatus = 5 + ExitStatusNotImplementedWatch ExitStatus = 6 + ExitStatusNotImplementedIncremental ExitStatus = 7 +) diff --git a/internal/execute/testsys_test.go b/internal/execute/testsys_test.go new file mode 100644 index 0000000000..2f258abff8 --- /dev/null +++ b/internal/execute/testsys_test.go @@ -0,0 +1,169 @@ +package execute_test + +import ( + "fmt" + "io" + "strings" + "testing/fstest" + + "github.com/microsoft/typescript-go/internal/bundled" + "github.com/microsoft/typescript-go/internal/vfs" + "github.com/microsoft/typescript-go/internal/vfs/vfstest" +) + +type FileMap map[string]string + +func newTestSys(fileOrFolderList FileMap, cwd string, args ...string) *testSys { + if cwd == "" { + cwd = "/home/src/workspaces/project" + } + mapFS := fstest.MapFS{} + fileList := []string{} + for name, content := range fileOrFolderList { + mapFS[strings.TrimPrefix(name, "/")] = &fstest.MapFile{ + Data: []byte(content), + } + fileList = append(fileList, name) + } + fs := bundled.WrapFS(vfstest.FromMapFS(mapFS, true /*useCaseSensitiveFileNames*/)) + return &testSys{ + fs: fs, + cwd: cwd, + files: fileList, + output: []string{}, + currentWrite: &strings.Builder{}, + } +} + +type testSys struct { + // todo: original has write to output as a string[] because the separations are needed for baselining + output []string + currentWrite *strings.Builder + serializedDiff map[string]string + fs vfs.FS + cwd string + files []string +} + +func (s *testSys) FS() vfs.FS { + return s.fs +} + +func (s *testSys) GetCurrentDirectory() string { + return s.cwd +} + +func (s *testSys) NewLine() string { + return "\n" +} + +func (s *testSys) Writer() io.Writer { + return s.currentWrite +} + +func (s *testSys) EndWrite() { + // todo: revisit if improving tsc/build/watch unittest baselines + s.output = append(s.output, s.currentWrite.String()) + s.currentWrite.Reset() +} + +func (s *testSys) serializeState(baseline *strings.Builder, order serializeOutputOrder) { + if order == serializeOutputOrderBefore { + s.serializeOutput(baseline) + } + s.diff(baseline) + if order == serializeOutputOrderAfter { + s.serializeOutput(baseline) + } + // todo watch + // this.serializeWatches(baseline); + // this.timeoutCallbacks.serialize(baseline); + // this.immediateCallbacks.serialize(baseline); + // this.pendingInstalls.serialize(baseline); + // this.service?.baseline(); +} + +func (s *testSys) baselineFS(baseline *strings.Builder) { + baseline.WriteString("\n\nCurrentFiles::") + err := s.FS().WalkDir(s.GetCurrentDirectory(), func(path string, d vfs.DirEntry, e error) error { + if d == nil { + return nil + } + if !d.IsDir() { + contents, ok := s.FS().ReadFile(path) + if !ok { + return e + } + baseline.WriteString("\n//// [" + path + "]\n" + contents) + } + return nil + }) + if err != nil { + panic("walkdir error during fs baseline") + } +} + +func (s *testSys) serializeOutput(baseline io.Writer) { + fmt.Fprint(baseline, "\nOutput::\n") + // todo screen clears + s.baselineOutputs(baseline, 0, len(s.output)) +} + +func (s *testSys) diff(baseline io.Writer) { + // todo: watch isnt implemented + // todo: not sure if this actually runs diff correctly, but don't really care atm because we aren't passing edits into the test, so we don't care abt diffs + snap := map[string]string{} + + err := s.FS().WalkDir(s.GetCurrentDirectory(), func(path string, d vfs.DirEntry, e error) error { + if d == nil { + return nil + } + + newContents, ok := s.FS().ReadFile(path) + if !ok { + return e + } + snap[path] = newContents + diffFSEntry(baseline, s.serializedDiff[path], newContents, path) + + return nil + }) + if err != nil { + panic("walkdir error during diff") + } + for path, oldDirContents := range s.serializedDiff { + if s.FS().FileExists(path) { + _, ok := s.FS().ReadFile(path) + if !ok { + // report deleted + diffFSEntry(baseline, oldDirContents, "", path) + } + } + } + s.serializedDiff = snap + fmt.Fprintln(baseline) +} + +func diffFSEntry(baseline io.Writer, oldDirContent string, newDirContent string, path string) { + // todo handle more cases of fs changes + if newDirContent == "" { + fmt.Fprint(baseline, `//// [`, path, `] deleted`, "\n") + } else if newDirContent == oldDirContent { + return + } else { + fmt.Fprint(baseline, `//// [`, path, `]\n`, newDirContent, "\n") + } +} + +func (s *testSys) baselineOutputs(baseline io.Writer, start int, end int) { + // todo sanitize sys output + fmt.Fprint(baseline, strings.Join(s.output[start:end], "\n")) +} + +type serializeOutputOrder int + +const ( + serializeOutputOrderNone serializeOutputOrder = iota + serializeOutputOrderBefore serializeOutputOrder = 1 + serializeOutputOrderAfter serializeOutputOrder = 2 +) diff --git a/internal/execute/tsc.go b/internal/execute/tsc.go new file mode 100644 index 0000000000..aaed741506 --- /dev/null +++ b/internal/execute/tsc.go @@ -0,0 +1,243 @@ +package execute + +import ( + "fmt" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/compiler" + "github.com/microsoft/typescript-go/internal/compiler/diagnostics" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/tsoptions" + "github.com/microsoft/typescript-go/internal/tspath" +) + +type cbType = func(p any) any + +func CommandLine(sys System, cb cbType, commandLineArgs []string) ExitStatus { + parsedCommandLine := tsoptions.ParseCommandLine(commandLineArgs, sys) + return executeCommandLineWorker(sys, cb, parsedCommandLine) +} + +func executeCommandLineWorker(sys System, cb cbType, commandLine *tsoptions.ParsedCommandLine) ExitStatus { + configFileName := "" + reportDiagnostic := createDiagnosticReporter(sys, commandLine.CompilerOptions().Pretty) + // if commandLine.Options().Locale != nil + + if len(commandLine.Errors) > 0 { + for _, e := range commandLine.Errors { + reportDiagnostic(e) + } + return ExitStatusDiagnosticsPresent_OutputsSkipped + } + + // if commandLine.Options().Init != nil + // if commandLine.Options().Version != nil + // if commandLine.Options().Help != nil || commandLine.Options().All != nil + // if commandLine.Options().Watch != nil && commandLine.Options().ListFilesOnly + + if commandLine.CompilerOptions().Project != "" { + if len(commandLine.FileNames()) != 0 { + reportDiagnostic(ast.NewCompilerDiagnostic(diagnostics.Option_project_cannot_be_mixed_with_source_files_on_a_command_line)) + return ExitStatusDiagnosticsPresent_OutputsSkipped + } + + fileOrDirectory := tspath.NormalizePath(commandLine.CompilerOptions().Project) + if fileOrDirectory != "" || sys.FS().DirectoryExists(fileOrDirectory) { + configFileName = tspath.CombinePaths(fileOrDirectory, "tsconfig.json") + if !sys.FS().FileExists(configFileName) { + reportDiagnostic(ast.NewCompilerDiagnostic(diagnostics.Cannot_find_a_tsconfig_json_file_at_the_current_directory_Colon_0, configFileName)) + return ExitStatusDiagnosticsPresent_OutputsSkipped + } + } else { + configFileName = fileOrDirectory + if !sys.FS().FileExists(configFileName) { + reportDiagnostic(ast.NewCompilerDiagnostic(diagnostics.The_specified_path_does_not_exist_Colon_0, fileOrDirectory)) + return ExitStatusDiagnosticsPresent_OutputsSkipped + } + } + } else if len(commandLine.FileNames()) == 0 { + searchPath := tspath.NormalizePath(sys.GetCurrentDirectory()) + configFileName = findConfigFile(searchPath, sys.FS().FileExists, "tsconfig.json") + } + + if configFileName == "" && len(commandLine.FileNames()) == 0 { + if commandLine.CompilerOptions().ShowConfig.IsTrue() { + reportDiagnostic(ast.NewCompilerDiagnostic(diagnostics.Cannot_find_a_tsconfig_json_file_at_the_current_directory_Colon_0, tspath.NormalizePath(sys.GetCurrentDirectory()))) + } else { + // print version + // print help + } + return ExitStatusDiagnosticsPresent_OutputsSkipped + } + + // !!! convert to options with absolute paths is usualy done here, but for ease of implementation, it's done in `tsoptions.ParseCommandLine` + compilerOptionsFromCommandLine := commandLine.CompilerOptions() + + if configFileName != "" { + extendedConfigCache := map[string]*tsoptions.ExtendedConfigCacheEntry{} + configParseResult, errors := getParsedCommandLineOfConfigFile(configFileName, compilerOptionsFromCommandLine, sys, extendedConfigCache) + if len(errors) != 0 { + // these are unrecoverable errors--exit to report them as diagnotics + for _, e := range errors { + reportDiagnostic(e) + } + return ExitStatusDiagnosticsPresent_OutputsGenerated + } + // if commandLineOptions.ShowConfig + // updateReportDiagnostic + if isWatchSet(configParseResult.CompilerOptions()) { + // todo watch + // return ExitStatusDiagnosticsPresent_OutputsSkipped + return ExitStatusNotImplementedWatch + } else if isIncrementalCompilation(configParseResult.CompilerOptions()) { + // todo performIncrementalCompilation + return ExitStatusNotImplementedIncremental + } else { + performCompilation( + sys, + cb, + configParseResult, + reportDiagnostic, + ) + } + } else { + if compilerOptionsFromCommandLine.ShowConfig.IsTrue() { + // write show config + return ExitStatusNotImplementedIncremental + } + // todo update reportDiagnostic + if isWatchSet(compilerOptionsFromCommandLine) { + // todo watch + // return ExitStatusDiagnosticsPresent_OutputsSkipped + return ExitStatusNotImplementedWatch + } else if isIncrementalCompilation(compilerOptionsFromCommandLine) { + // todo incremental + return ExitStatusNotImplementedIncremental + } else { + commandLine.SetCompilerOptions(compilerOptionsFromCommandLine) + performCompilation( + sys, + cb, + commandLine, + reportDiagnostic, + ) + } + } + + return ExitStatusSuccess +} + +func findConfigFile(searchPath string, fileExists func(string) bool, configName string) string { + result, ok := tspath.ForEachAncestorDirectory(searchPath, func(ancestor string) (string, bool) { + fullConfigName := tspath.CombinePaths(ancestor, configName) + if fileExists(fullConfigName) { + return fullConfigName, true + } + return fullConfigName, false + }) + if !ok { + return "" + } + return result +} + +// Reads the config file and reports errors. Exits if the config file cannot be found +func getParsedCommandLineOfConfigFile(configFileName string, options *core.CompilerOptions, sys System, extendedConfigCache map[string]*tsoptions.ExtendedConfigCacheEntry) (*tsoptions.ParsedCommandLine, []*ast.Diagnostic) { + errors := []*ast.Diagnostic{} + configFileText, errors := tsoptions.TryReadFile(configFileName, sys.FS().ReadFile, errors) + if len(errors) > 0 { + // these are unrecoverable errors--exit to report them as diagnotics + return nil, errors + } + + tsConfigSourceFile := tsoptions.NewTsconfigSourceFileFromFilePath(configFileName, configFileText) + cwd := sys.GetCurrentDirectory() + tsConfigSourceFile.SourceFile.SetPath(tspath.ToPath(configFileName, cwd, sys.FS().UseCaseSensitiveFileNames())) + // tsConfigSourceFile.resolvedPath = tsConfigSourceFile.FileName() + // tsConfigSourceFile.originalFileName = tsConfigSourceFile.FileName() + return tsoptions.ParseJsonSourceFileConfigFileContent( + tsConfigSourceFile, + sys, + tspath.GetNormalizedAbsolutePath(tspath.GetDirectoryPath(configFileName), cwd), + options, + tspath.GetNormalizedAbsolutePath(configFileName, cwd), + nil, + nil, + extendedConfigCache, + ), nil +} + +func performCompilation(sys System, cb cbType, config *tsoptions.ParsedCommandLine, reportDiagnostic diagnosticReporter) ExitStatus { + host := compiler.NewCompilerHost(config.CompilerOptions(), sys.GetCurrentDirectory(), sys.FS()) + // todo: cache, statistics, tracing + program := compiler.NewProgramFromParsedCommandLine(config, host) + options := program.Options() + allDiagnostics := program.GetOptionsDiagnostics() + + // todo: early exit logic and append diagnostics + diagnostics := program.GetSyntacticDiagnostics(nil) + if len(diagnostics) == 0 { + diagnostics = append(diagnostics, program.GetOptionsDiagnostics()...) + if options.ListFilesOnly.IsFalse() { + // program.GetBindDiagnostics(nil) + diagnostics = append(diagnostics, program.GetGlobalDiagnostics()...) + } + } + if len(diagnostics) == 0 { + diagnostics = append(diagnostics, program.GetSemanticDiagnostics(nil)...) + } + // TODO: declaration diagnostics + if len(diagnostics) == 0 && options.NoEmit == core.TSTrue && (options.Declaration.IsTrue() && options.Composite.IsTrue()) { + return ExitStatusNotImplemented + // addRange(allDiagnostics, program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken)); + } + + emitResult := &compiler.EmitResult{EmitSkipped: true, Diagnostics: []*ast.Diagnostic{}} + if options.ListFilesOnly.IsFalse() { + // todo emit not fully implemented + emitResult = program.Emit(&compiler.EmitOptions{}) + } + diagnostics = append(diagnostics, emitResult.Diagnostics...) + + allDiagnostics = append(allDiagnostics, diagnostics...) + if allDiagnostics != nil { + allDiagnostics = compiler.SortAndDeduplicateDiagnostics(allDiagnostics) + for _, diagnostic := range allDiagnostics { + reportDiagnostic(diagnostic) + } + } + + // !!! if (write) + if sys.Writer() != nil { + for _, file := range emitResult.EmittedFiles { + fmt.Fprint(sys.Writer(), "TSFILE: ", tspath.GetNormalizedAbsolutePath(file, sys.GetCurrentDirectory())) + } + // todo: listFiles(program, sys.Writer()) + } + + createReportErrorSummary(sys, config.CompilerOptions())(allDiagnostics) + + reportStatistics(sys, program) + if cb != nil { + cb(program) + } + + if emitResult.EmitSkipped && diagnostics != nil && len(diagnostics) > 0 { + return ExitStatusDiagnosticsPresent_OutputsSkipped + } else if len(diagnostics) > 0 { + return ExitStatusDiagnosticsPresent_OutputsGenerated + } + return ExitStatusSuccess +} + +// func isBuildCommand(args []string) bool { +// return len(args) > 0 && args[0] == "build" +// } + +func isWatchSet(options *core.CompilerOptions) bool { + return options.Watch.IsTrue() +} + +func isIncrementalCompilation(options *core.CompilerOptions) bool { + return options.Incremental.IsTrue() +} diff --git a/internal/execute/tsc_test.go b/internal/execute/tsc_test.go new file mode 100644 index 0000000000..f3e4cfd2b7 --- /dev/null +++ b/internal/execute/tsc_test.go @@ -0,0 +1,212 @@ +package execute_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/bundled" +) + +func TestTsc(t *testing.T) { + t.Parallel() + if !bundled.Embedded { + // Without embedding, we'd need to read all of the lib files out from disk into the MapFS. + // Just skip this for now. + t.Skip("bundled files are not embedded") + } + + testCases := []*tscInput{ + { + scenario: "commandLine", + subScenario: "show help with ExitStatus.DiagnosticsPresent_OutputsSkipped", + sys: newTestSys(nil, ""), + // , { + // environmentVariables: new Map([["TS_TEST_TERMINAL_WIDTH", "120"]]), + // }), + commandLineArgs: nil, + }, + { + scenario: "commandLine", + subScenario: "show help with ExitStatus.DiagnosticsPresent_OutputsSkipped when host can't provide terminal width", + sys: newTestSys(nil, ""), + commandLineArgs: nil, + }, + { + scenario: "commandLine", + subScenario: "does not add color when NO_COLOR is set", + sys: newTestSys(nil, ""), + // , { + // environmentVariables: new Map([["NO_COLOR", "true"]]), + // }), + commandLineArgs: nil, + }, + { + scenario: "commandLine", + subScenario: "does not add color when NO_COLOR is set", + sys: newTestSys(nil, ""), + // , { + // environmentVariables: new Map([["NO_COLOR", "true"]]), + // } + // ), + commandLineArgs: nil, + }, + { + scenario: "commandLine", + subScenario: "when build not first argument", + sys: newTestSys(nil, ""), + commandLineArgs: []string{"--verbose", "--build"}, + }, + { + scenario: "commandLine", + subScenario: "help", + sys: newTestSys(nil, ""), + commandLineArgs: []string{"--help"}, + }, + { + scenario: "commandLine", + subScenario: "help all", + sys: newTestSys(nil, ""), + commandLineArgs: []string{"--help", "--all"}, + }, + } + + for _, testCase := range testCases { + testCase.verify(t) + } +} + +func TestNoEmit(t *testing.T) { + t.Parallel() + if !bundled.Embedded { + // Without embedding, we'd need to read all of the lib files out from disk into the MapFS. + // Just skip this for now. + t.Skip("bundled files are not embedded") + } + + (&tscInput{ + scenario: "noEmit", + subScenario: "when project has strict true", + sys: newTestSys(FileMap{ + "/home/src/workspaces/project/tsconfig.json": `{ + "compilerOptions": { + "incremental": true, + "strict": true, + }, +}`, + "/home/src/workspaces/project/class1.ts": `export class class1 {}`, + }, ""), + commandLineArgs: []string{"--noEmit"}, + }).verify(t) +} + +func TestProjectReferences(t *testing.T) { + t.Parallel() + if !bundled.Embedded { + // Without embedding, we'd need to read all of the lib files out from disk into the MapFS. + // Just skip this for now. + t.Skip("bundled files are not embedded") + } + + (&tscInput{ + scenario: "projectReferences", + subScenario: "when project references composite project with noEmit", + sys: newTestSys(FileMap{ + "/home/src/workspaces/solution/src/utils/index.ts": "export const x = 10;", + "/home/src/workspaces/solution/src/utils/tsconfig.json": `{ + "compilerOptions": { + "composite": true, + "noEmit": true, + }, +})`, + "/home/src/workspaces/solution/project/index.ts": `import { x } from "../utils";`, + "/home/src/workspaces/solution/project/tsconfig.json": `{ + "references": [ + { "path": "../utils" }, + ], + }), +},`, + }, + "/home/src/workspaces/solution", + ), + commandLineArgs: []string{"--p", "project"}, + }).verify(t) +} + +// func TestExtends(t *testing.T) { +// t.Parallel() +// if !bundled.Embedded { +// // Without embedding, we'd need to read all of the lib files out from disk into the MapFS. +// // Just skip this for now. +// t.Skip("bundled files are not embedded") +// } +// +// extendsSys := NewTestSys(FileMap{ +// "/home/src/projects/configs/first/tsconfig.json": `{ +// extends: "../second/tsconfig.json", +// include: ["${configDir}/src"], +// compilerOptions: { +// typeRoots: ["root1", "${configDir}/root2", "root3"], +// types: [], +// }, +// }`, +// "/home/src/projects/configs/second/tsconfig.json": `{ +// files: ["${configDir}/main.ts"], +// compilerOptions: { +// declarationDir: "${configDir}/decls", +// paths: { +// "@myscope/*": ["${configDir}/types/*"], +// "other/*": ["other/*"], +// }, +// baseUrl: "${configDir}", +// }, +// watchOptions: { +// excludeFiles: ["${configDir}/main.ts"], +// }, +// }`, +// "/home/src/projects/myproject/tsconfig.json": `{ +// extends: "../configs/first/tsconfig.json", +// compilerOptions: { +// declaration: true, +// outDir: "outDir", +// traceResolution: true, +// }, +// }`, +// +// "/home/src/projects/myproject/main.ts": ` +// // some comment +// export const y = 10; +// import { x } from "@myscope/sometype"; +// `, +// "/home/src/projects/myproject/src/secondary.ts": ` +// // some comment +// export const z = 10; +// import { k } from "other/sometype2"; +// `, +// "/home/src/projects/myproject/types/sometype.ts": ` +// export const x = 10; +// `, +// "/home/src/projects/myproject/root2/other/sometype2/index.d.ts": ` +// export const k = 10; +// `, +// }, "/home/src/projects/myproject" ); +// +// cases := []tscInput{{ +// scenario: "extends", +// subScenario: "configDir template", +// sys: extendsSys, +// commandLineArgs: []string{"--explainFiles"}, +// },{ +// scenario: "extends", +// subScenario: "configDir template showConfig", +// sys: extendsSys, +// commandLineArgs: []string{"--showConfig"}, +// },{ +// scenario: "extends", +// subScenario: "configDir template with commandline", +// sys: extendsSys, +// commandLineArgs: []string{"--explainFiles", "--outDir", "${configDir}/outDir"}, +// }} +// +// for _, c := range cases { +// c.verify(t) +// } +// } diff --git a/internal/execute/verifytsc_test.go b/internal/execute/verifytsc_test.go new file mode 100644 index 0000000000..125807ea50 --- /dev/null +++ b/internal/execute/verifytsc_test.go @@ -0,0 +1,108 @@ +package execute_test + +import ( + "encoding/json" + "fmt" + "path/filepath" + "strings" + "testing" + + "github.com/microsoft/typescript-go/internal/execute" + "github.com/microsoft/typescript-go/internal/testutil/baseline" +) + +type testTscCompile struct { + commandLineArgs []string + sys execute.System + modifySystem func(fs execute.System) + computeDtsSignatures bool + getWrittenFiles bool + baselineSourceMap bool + baselineReadFileCalls bool + baselinePrograms bool + baselineDependencies bool + compile func(sys execute.System) // CommandLineCallbacks["getPrograms"] tscWatchSystem +} + +type testTscEdit struct { + edit func(execute.System) + caption string + commandLineArgs []string + // todo explaination func +} + +type tscInput struct { + scenario string + subScenario string + commandLineArgs []string + sys *testSys + // edits []*testTscEdit +} + +func (test *tscInput) verify(t *testing.T) { + t.Helper() + t.Run(test.getTestName(), func(t *testing.T) { + t.Parallel() + t.Run("baseline for the tsc compiles", func(t *testing.T) { + t.Parallel() + // initial test tsc compile + baselineBuilder := test.startBaseline() + // baseline push sys.GetExecutingFilepath + fmt.Fprint(baselineBuilder, strings.Join(test.commandLineArgs, " ")+"\n") + // if (input.baselineSourceMap) generateSourceMapBasleineFiles + + parsedCommandLine, exit := execute.CommandLineTest(test.sys, nil, test.commandLineArgs) + baselineBuilder.WriteString("\n\nExitStatus:: " + fmt.Sprint(exit)) + + compilerOptionsString, _ := json.MarshalIndent(parsedCommandLine.CompilerOptions(), "", " ") + baselineBuilder.WriteString("\n\nCompilerOptions::") + baselineBuilder.Write(compilerOptionsString) + + test.sys.serializeState(baselineBuilder, serializeOutputOrderBefore) + options, name := test.getBaselineName("") + baseline.Run(t, name, baselineBuilder.String(), options) + }) + }) + // skip edits for now + // todo: refactor edits into a different function + // if input.edits != nil && len(input.edits) > 0 { + // t.Run("tsc invocation after edit and clean build correctness", func(t *testing.T) { + // for i, edit := range input.edits { + // t.Run(edit.caption, func(t *testing.T) { + // // todo + // }) + // } + // }) + // } +} + +func (test *tscInput) getTestName() string { + return "tsc " + strings.Join(test.commandLineArgs, " ") + " " + test.scenario + ":: " + test.subScenario +} + +func (test *tscInput) getBaselineName(suffix string) (baseline.Options, string) { + commandName := "tsc" + // todo build + // if isBuildCommand(v.data.commandLineArgs) { + // commandName = "tsbuild" + // } + watch := "" + // todo watch + // if isWatch(v.data.commandLineArgs) { watch = "Watch" } + + return baseline.Options{Subfolder: filepath.Join(commandName+watch, test.scenario)}, + strings.ReplaceAll(test.subScenario, " ", "-") + suffix + ".js" +} + +func (test *tscInput) startBaseline() *strings.Builder { + s := &strings.Builder{} + fmt.Fprint( + s, + "\ncurrentDirectory::", + test.sys.GetCurrentDirectory(), + "\nuseCaseSensitiveFileNames::", + test.sys.FS().UseCaseSensitiveFileNames(), + "\nInput::", + ) + return s +} diff --git a/internal/testutil/tsbaseline/error_baseline.go b/internal/testutil/tsbaseline/error_baseline.go index 6019047ab0..420f1701e7 100644 --- a/internal/testutil/tsbaseline/error_baseline.go +++ b/internal/testutil/tsbaseline/error_baseline.go @@ -90,7 +90,7 @@ func iterateErrorBaseline(t *testing.T, inputFiles []*harnessutil.TestFile, inpu var result []string outputErrorText := func(diag *ast.Diagnostic) { - message := flattenDiagnosticMessage(diag, harnessNewLine) + message := diagnosticwriter.FlattenDiagnosticMessage(diag, harnessNewLine) var errLines []string for _, line := range strings.Split(removeTestPathPrefixes(message, false), "\n") { @@ -111,7 +111,7 @@ func iterateErrorBaseline(t *testing.T, inputFiles []*harnessutil.TestFile, inpu if len(location) > 0 && isDefaultLibraryFile(info.File().FileName()) { location = diagnosticsLocationPattern.ReplaceAllString(location, "$1:--:--") } - errLines = append(errLines, fmt.Sprintf("!!! related TS%d%s: %s", info.Code(), location, flattenDiagnosticMessage(info, harnessNewLine))) + errLines = append(errLines, fmt.Sprintf("!!! related TS%d%s: %s", info.Code(), location, diagnosticwriter.FlattenDiagnosticMessage(info, harnessNewLine))) } for _, e := range errLines { @@ -242,12 +242,6 @@ func iterateErrorBaseline(t *testing.T, inputFiles []*harnessutil.TestFile, inpu return result } -func flattenDiagnosticMessage(d *ast.Diagnostic, newLine string) string { - var output strings.Builder - diagnosticwriter.WriteFlattenedDiagnosticMessage(&output, d, newLine) - return output.String() -} - func formatLocation(file *ast.SourceFile, pos int, formatOpts *diagnosticwriter.FormattingOptions, writeWithStyleAndReset diagnosticwriter.FormattedWriter) string { var output strings.Builder diagnosticwriter.WriteLocation(&output, file, pos, formatOpts, writeWithStyleAndReset) diff --git a/internal/tsoptions/commandlineparser.go b/internal/tsoptions/commandlineparser.go index 2f62cb17fa..875b1e2520 100644 --- a/internal/tsoptions/commandlineparser.go +++ b/internal/tsoptions/commandlineparser.go @@ -11,8 +11,6 @@ import ( "github.com/microsoft/typescript-go/internal/vfs" ) -type OptionsBase map[string]any // CompilerOptionsValue|TsConfigSourceFile - func (p *CommandLineParser) AlternateMode() *AlternateModeDiagnostics { return p.workerDiagnostics.didYouMean.alternateMode } @@ -50,20 +48,32 @@ func (p *CommandLineParser) GetOptionsNameMap() *NameMap { type CommandLineParser struct { workerDiagnostics *ParseCommandLineWorkerDiagnostics fs vfs.FS - options OptionsBase - // todo: watchOptions OptionsBase + options map[string]any + // todo: watchOptions map[string]any fileNames []string errors []*ast.Diagnostic } func ParseCommandLine( commandLine []string, - fs vfs.FS, + host ParseConfigHost, ) *ParsedCommandLine { - // this function should convert commandLineWorker output to compileroptions - // todo: return correct type (waiting on shared tsconfig parsing utilities) - // parseCommandLineWorker() - return &ParsedCommandLine{} + if commandLine == nil { + commandLine = []string{} + } + parser := parseCommandLineWorker(CompilerOptionsDidYouMeanDiagnostics, commandLine, host.FS()) + optionsWithAbsolutePaths := convertToOptionsWithAbsolutePaths(parser.options, commandLineCompilerOptionsMap, host.GetCurrentDirectory()) + o, d := convertOptionsFromJson(commandLineCompilerOptionsMap, optionsWithAbsolutePaths, host.GetCurrentDirectory(), &core.CompilerOptions{}) + return &ParsedCommandLine{ + ParsedConfig: &core.ParsedOptions{ + CompilerOptions: o, + FileNames: parser.fileNames, + }, + ConfigFile: nil, + Errors: append(parser.errors, d...), + Raw: parser.options, // todo: keep optionsBase incase needed later + CompileOnSave: nil, + } } func parseCommandLineWorker( @@ -75,7 +85,7 @@ func parseCommandLineWorker( fs: fs, workerDiagnostics: parseCommandLineWithDiagnostics, fileNames: []string{}, - options: OptionsBase{}, + options: map[string]any{}, errors: []*ast.Diagnostic{}, } parser.parseStrings(commandLine) diff --git a/internal/tsoptions/parsedcommandline.go b/internal/tsoptions/parsedcommandline.go index 1d142f3727..076275bcdd 100644 --- a/internal/tsoptions/parsedcommandline.go +++ b/internal/tsoptions/parsedcommandline.go @@ -19,22 +19,6 @@ type ParsedCommandLine struct { // TypeAquisition *core.TypeAcquisition } -func NewParsedCommandLine( - options *core.ParsedOptions, - configFile *ast.SourceFile, - errors []*ast.Diagnostic, - raw any, - compileOnSave *bool, -) ParsedCommandLine { - return ParsedCommandLine{ - ParsedConfig: options, - ConfigFile: configFile, - Errors: errors, - Raw: raw, - CompileOnSave: compileOnSave, - } -} - func (p *ParsedCommandLine) SetParsedOptions(o *core.ParsedOptions) { p.ParsedConfig = o } diff --git a/internal/tsoptions/parsinghelpers.go b/internal/tsoptions/parsinghelpers.go index d3d6ce88eb..ba6a082e35 100644 --- a/internal/tsoptions/parsinghelpers.go +++ b/internal/tsoptions/parsinghelpers.go @@ -5,6 +5,7 @@ import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/tspath" ) func parseTristate(value any) core.Tristate { @@ -386,3 +387,27 @@ func MergeCompilerOptions(targetOptions, sourceOptions *core.CompilerOptions) *c } return targetOptions } + +func convertToOptionsWithAbsolutePaths(optionsBase map[string]any, optionMap map[string]*CommandLineOption, cwd string) map[string]any { + // !!! convert to options with absolute paths was previously done with `CompilerOptions` object, but for ease of implementation, we do it pre-conversion. + // !!! Revisit this choice if/when refactoring when conversion is done in tsconfig parsing + if optionsBase == nil { + return nil + } + for o, v := range optionsBase { + option := optionMap[o] + if option == nil || !option.isFilePath { + continue + } + if option.Kind == "list" { + if arr, ok := v.([]string); ok { + optionsBase[o] = core.Map(arr, func(item string) string { + return tspath.GetNormalizedAbsolutePath(item, cwd) + }) + } + } else { + optionsBase[o] = tspath.GetNormalizedAbsolutePath(v.(string), cwd) + } + } + return optionsBase +} diff --git a/internal/tsoptions/tsconfigparsing.go b/internal/tsoptions/tsconfigparsing.go index 891f9b4eb6..1254180e42 100644 --- a/internal/tsoptions/tsconfigparsing.go +++ b/internal/tsoptions/tsconfigparsing.go @@ -103,7 +103,7 @@ type fileExtensionInfo struct { isMixedContent bool scriptKind core.ScriptKind } -type extendedConfigCacheEntry struct { +type ExtendedConfigCacheEntry struct { extendedResult *TsConfigSourceFile extendedConfig *parsedTsconfig } @@ -208,6 +208,13 @@ func tsconfigToSourceFile(tsconfigSourceFile *TsConfigSourceFile) *ast.SourceFil return tsconfigSourceFile.SourceFile } +func NewTsconfigSourceFileFromFilePath(configFileName string, configSourceText string) *TsConfigSourceFile { + sourceFile := parser.ParseJSONText(configFileName, configSourceText) + return &TsConfigSourceFile{ + SourceFile: sourceFile, + } +} + type jsonConversionNotifier struct { rootOptions *CommandLineOption onPropertySet func(keyText string, value any, propertyAssignment *ast.PropertyAssignment, parentOption *CommandLineOption, option *CommandLineOption) (any, []*ast.Diagnostic) @@ -587,7 +594,7 @@ type resolverHost struct { func (r *resolverHost) Trace(msg string) {} -func ParseJsonSourceFileConfigFileContent(sourceFile *TsConfigSourceFile, host ParseConfigHost, basePath string, existingOptions *core.CompilerOptions, configFileName string, resolutionStack []tspath.Path, extraFileExtensions []fileExtensionInfo, extendedConfigCache map[string]*extendedConfigCacheEntry) *ParsedCommandLine { +func ParseJsonSourceFileConfigFileContent(sourceFile *TsConfigSourceFile, host ParseConfigHost, basePath string, existingOptions *core.CompilerOptions, configFileName string, resolutionStack []tspath.Path, extraFileExtensions []fileExtensionInfo, extendedConfigCache map[string]*ExtendedConfigCacheEntry) *ParsedCommandLine { // tracing?.push(tracing.Phase.Parse, "parseJsonSourceFileConfigFileContent", { path: sourceFile.fileName }); result := parseJsonConfigFileContentWorker(nil /*json*/, sourceFile, host, basePath, existingOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache) // tracing?.pop(); @@ -727,7 +734,7 @@ func convertPropertyValueToJson(sourceFile *ast.SourceFile, valueExpression *ast // jsonNode: The contents of the config file to parse // host: Instance of ParseConfigHost used to enumerate files in folder. // basePath: A root directory to resolve relative path entries in the config file to. e.g. outDir -func ParseJsonConfigFileContent(json any, host ParseConfigHost, basePath string, existingOptions *core.CompilerOptions, configFileName string, resolutionStack []tspath.Path, extraFileExtensions []fileExtensionInfo, extendedConfigCache map[string]*extendedConfigCacheEntry) *ParsedCommandLine { +func ParseJsonConfigFileContent(json any, host ParseConfigHost, basePath string, existingOptions *core.CompilerOptions, configFileName string, resolutionStack []tspath.Path, extraFileExtensions []fileExtensionInfo, extendedConfigCache map[string]*ExtendedConfigCacheEntry) *ParsedCommandLine { result := parseJsonConfigFileContentWorker(parseJsonToStringKey(json), nil /*sourceFile*/, host, basePath, existingOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache) return result } @@ -813,7 +820,7 @@ func getExtendedConfig( extendedConfigPath string, host ParseConfigHost, resolutionStack []string, - extendedConfigCache map[string]*extendedConfigCacheEntry, + extendedConfigCache map[string]*ExtendedConfigCacheEntry, result *extendsResult, ) (*parsedTsconfig, []*ast.Diagnostic) { var path string @@ -838,7 +845,7 @@ func getExtendedConfig( errors = append(errors, err...) } if extendedConfigCache != nil { - extendedConfigCache[path] = &extendedConfigCacheEntry{ + extendedConfigCache[path] = &ExtendedConfigCacheEntry{ extendedResult: extendedResult, extendedConfig: extendedConfig, } @@ -868,7 +875,7 @@ func parseConfig( basePath string, configFileName string, resolutionStack []string, - extendedConfigCache map[string]*extendedConfigCacheEntry, + extendedConfigCache map[string]*ExtendedConfigCacheEntry, ) (*parsedTsconfig, []*ast.Diagnostic) { basePath = tspath.NormalizeSlashes(basePath) resolvedPath := tspath.GetNormalizedAbsolutePath(configFileName, basePath) @@ -1011,7 +1018,7 @@ func parseJsonConfigFileContentWorker( configFileName string, resolutionStack []tspath.Path, extraFileExtensions []fileExtensionInfo, - extendedConfigCache map[string]*extendedConfigCacheEntry, + extendedConfigCache map[string]*ExtendedConfigCacheEntry, ) *ParsedCommandLine { // Debug.assert((json === undefined && sourceFile !== undefined) || (json !== undefined && sourceFile === undefined)); var errors []*ast.Diagnostic diff --git a/testdata/baselines/reference/tsc/commandLine/does-not-add-color-when-NO_COLOR-is-set.js b/testdata/baselines/reference/tsc/commandLine/does-not-add-color-when-NO_COLOR-is-set.js new file mode 100644 index 0000000000..1dd14c5c24 --- /dev/null +++ b/testdata/baselines/reference/tsc/commandLine/does-not-add-color-when-NO_COLOR-is-set.js @@ -0,0 +1,134 @@ + +currentDirectory::/home/src/workspaces/project +useCaseSensitiveFileNames::true +Input:: + + +ExitStatus:: 1 + +CompilerOptions::{ + "allowJs": null, + "allowArbitraryExtensions": null, + "allowSyntheticDefaultImports": null, + "allowImportingTsExtensions": null, + "allowNonTsExtensions": null, + "allowUmdGlobalAccess": null, + "allowUnreachableCode": null, + "allowUnusedLabels": null, + "assumeChangesOnlyAffectDirectDependencies": null, + "alwaysStrict": null, + "baseUrl": "", + "build": null, + "checkJs": null, + "customConditions": null, + "composite": null, + "emitDeclarationOnly": null, + "emitBOM": null, + "emitDecoratorMetadata": null, + "downlevelIteration": null, + "declaration": null, + "declarationDir": "", + "declarationMap": null, + "disableSizeLimit": null, + "disableSourceOfProjectReferenceRedirect": null, + "disableSolutionSearching": null, + "disableReferencedProjectLoad": null, + "esModuleInterop": null, + "exactOptionalPropertyTypes": null, + "experimentalDecorators": null, + "forceConsistentCasingInFileNames": null, + "isolatedModules": null, + "isolatedDeclarations": null, + "ignoreDeprecations": "", + "importHelpers": null, + "inlineSourceMap": null, + "inlineSources": null, + "init": null, + "incremental": null, + "jsx": 0, + "jsxFactory": "", + "jsxFragmentFactory": "", + "jsxImportSource": "", + "keyofStringsOnly": null, + "lib": null, + "locale": "", + "mapRoot": "", + "module": 0, + "moduleResolution": 0, + "moduleSuffixes": null, + "moduleDetectionKind": 0, + "newLine": 0, + "noEmit": null, + "noCheck": null, + "noErrorTruncation": null, + "noFallthroughCasesInSwitch": null, + "noImplicitAny": null, + "noImplicitThis": null, + "noImplicitReturns": null, + "noEmitHelpers": null, + "noLib": null, + "noPropertyAccessFromIndexSignature": null, + "noUncheckedIndexedAccess": null, + "noEmitOnError": null, + "noUnusedLocals": null, + "noUnusedParameters": null, + "noResolve": null, + "noImplicitOverride": null, + "noUncheckedSideEffectImports": null, + "out": "", + "outDir": "", + "outFile": "", + "paths": null, + "preserveConstEnums": null, + "preserveSymlinks": null, + "project": "", + "resolveJsonModule": null, + "resolvePackageJsonExports": null, + "resolvePackageJsonImports": null, + "removeComments": null, + "rewriteRelativeImportExtensions": null, + "reactNamespace": "", + "rootDir": "", + "rootDirs": null, + "skipLibCheck": null, + "strict": null, + "strictBindCallApply": null, + "strictBuiltinIteratorReturn": null, + "strictFunctionTypes": null, + "strictNullChecks": null, + "strictPropertyInitialization": null, + "stripInternal": null, + "skipDefaultLibCheck": null, + "sourceMap": null, + "sourceRoot": "", + "suppressOutputPathCheck": null, + "target": 0, + "traceResolution": null, + "tsBuildInfoFile": "", + "typeRoots": null, + "types": null, + "useDefineForClassFields": null, + "useUnknownInCatchVariables": null, + "verbatimModuleSyntax": null, + "maxNodeModuleJsDepth": null, + "configFilePath": "", + "noDtsResolution": null, + "pathsBasePath": "", + "diagnostics": null, + "extendedDiagnostics": null, + "generateCpuProfile": "", + "generateTrace": "", + "listEmittedFiles": null, + "listFiles": null, + "explainFiles": null, + "listFilesOnly": null, + "noEmitForJsFiles": null, + "preserveWatchOutput": null, + "pretty": null, + "version": null, + "watch": null, + "showConfig": null, + "tscBuild": null +} +Output:: + diff --git a/testdata/baselines/reference/tsc/commandLine/help-all.js b/testdata/baselines/reference/tsc/commandLine/help-all.js new file mode 100644 index 0000000000..1fcfcf89d3 --- /dev/null +++ b/testdata/baselines/reference/tsc/commandLine/help-all.js @@ -0,0 +1,135 @@ + +currentDirectory::/home/src/workspaces/project +useCaseSensitiveFileNames::true +Input::--help --all + + +ExitStatus:: 1 + +CompilerOptions::{ + "allowJs": null, + "allowArbitraryExtensions": null, + "allowSyntheticDefaultImports": null, + "allowImportingTsExtensions": null, + "allowNonTsExtensions": null, + "allowUmdGlobalAccess": null, + "allowUnreachableCode": null, + "allowUnusedLabels": null, + "assumeChangesOnlyAffectDirectDependencies": null, + "alwaysStrict": null, + "baseUrl": "", + "build": null, + "checkJs": null, + "customConditions": null, + "composite": null, + "emitDeclarationOnly": null, + "emitBOM": null, + "emitDecoratorMetadata": null, + "downlevelIteration": null, + "declaration": null, + "declarationDir": "", + "declarationMap": null, + "disableSizeLimit": null, + "disableSourceOfProjectReferenceRedirect": null, + "disableSolutionSearching": null, + "disableReferencedProjectLoad": null, + "esModuleInterop": null, + "exactOptionalPropertyTypes": null, + "experimentalDecorators": null, + "forceConsistentCasingInFileNames": null, + "isolatedModules": null, + "isolatedDeclarations": null, + "ignoreDeprecations": "", + "importHelpers": null, + "inlineSourceMap": null, + "inlineSources": null, + "init": null, + "incremental": null, + "jsx": 0, + "jsxFactory": "", + "jsxFragmentFactory": "", + "jsxImportSource": "", + "keyofStringsOnly": null, + "lib": null, + "locale": "", + "mapRoot": "", + "module": 0, + "moduleResolution": 0, + "moduleSuffixes": null, + "moduleDetectionKind": 0, + "newLine": 0, + "noEmit": null, + "noCheck": null, + "noErrorTruncation": null, + "noFallthroughCasesInSwitch": null, + "noImplicitAny": null, + "noImplicitThis": null, + "noImplicitReturns": null, + "noEmitHelpers": null, + "noLib": null, + "noPropertyAccessFromIndexSignature": null, + "noUncheckedIndexedAccess": null, + "noEmitOnError": null, + "noUnusedLocals": null, + "noUnusedParameters": null, + "noResolve": null, + "noImplicitOverride": null, + "noUncheckedSideEffectImports": null, + "out": "", + "outDir": "", + "outFile": "", + "paths": null, + "preserveConstEnums": null, + "preserveSymlinks": null, + "project": "", + "resolveJsonModule": null, + "resolvePackageJsonExports": null, + "resolvePackageJsonImports": null, + "removeComments": null, + "rewriteRelativeImportExtensions": null, + "reactNamespace": "", + "rootDir": "", + "rootDirs": null, + "skipLibCheck": null, + "strict": null, + "strictBindCallApply": null, + "strictBuiltinIteratorReturn": null, + "strictFunctionTypes": null, + "strictNullChecks": null, + "strictPropertyInitialization": null, + "stripInternal": null, + "skipDefaultLibCheck": null, + "sourceMap": null, + "sourceRoot": "", + "suppressOutputPathCheck": null, + "target": 0, + "traceResolution": null, + "tsBuildInfoFile": "", + "typeRoots": null, + "types": null, + "useDefineForClassFields": null, + "useUnknownInCatchVariables": null, + "verbatimModuleSyntax": null, + "maxNodeModuleJsDepth": null, + "configFilePath": "", + "noDtsResolution": null, + "pathsBasePath": "", + "diagnostics": null, + "extendedDiagnostics": null, + "generateCpuProfile": "", + "generateTrace": "", + "listEmittedFiles": null, + "listFiles": null, + "explainFiles": null, + "listFilesOnly": null, + "noEmitForJsFiles": null, + "preserveWatchOutput": null, + "pretty": null, + "version": null, + "watch": null, + "showConfig": null, + "tscBuild": null +} +Output:: +error TS6266: Option 'help' can only be specified on command line. + diff --git a/testdata/baselines/reference/tsc/commandLine/help.js b/testdata/baselines/reference/tsc/commandLine/help.js new file mode 100644 index 0000000000..7337527cbd --- /dev/null +++ b/testdata/baselines/reference/tsc/commandLine/help.js @@ -0,0 +1,135 @@ + +currentDirectory::/home/src/workspaces/project +useCaseSensitiveFileNames::true +Input::--help + + +ExitStatus:: 1 + +CompilerOptions::{ + "allowJs": null, + "allowArbitraryExtensions": null, + "allowSyntheticDefaultImports": null, + "allowImportingTsExtensions": null, + "allowNonTsExtensions": null, + "allowUmdGlobalAccess": null, + "allowUnreachableCode": null, + "allowUnusedLabels": null, + "assumeChangesOnlyAffectDirectDependencies": null, + "alwaysStrict": null, + "baseUrl": "", + "build": null, + "checkJs": null, + "customConditions": null, + "composite": null, + "emitDeclarationOnly": null, + "emitBOM": null, + "emitDecoratorMetadata": null, + "downlevelIteration": null, + "declaration": null, + "declarationDir": "", + "declarationMap": null, + "disableSizeLimit": null, + "disableSourceOfProjectReferenceRedirect": null, + "disableSolutionSearching": null, + "disableReferencedProjectLoad": null, + "esModuleInterop": null, + "exactOptionalPropertyTypes": null, + "experimentalDecorators": null, + "forceConsistentCasingInFileNames": null, + "isolatedModules": null, + "isolatedDeclarations": null, + "ignoreDeprecations": "", + "importHelpers": null, + "inlineSourceMap": null, + "inlineSources": null, + "init": null, + "incremental": null, + "jsx": 0, + "jsxFactory": "", + "jsxFragmentFactory": "", + "jsxImportSource": "", + "keyofStringsOnly": null, + "lib": null, + "locale": "", + "mapRoot": "", + "module": 0, + "moduleResolution": 0, + "moduleSuffixes": null, + "moduleDetectionKind": 0, + "newLine": 0, + "noEmit": null, + "noCheck": null, + "noErrorTruncation": null, + "noFallthroughCasesInSwitch": null, + "noImplicitAny": null, + "noImplicitThis": null, + "noImplicitReturns": null, + "noEmitHelpers": null, + "noLib": null, + "noPropertyAccessFromIndexSignature": null, + "noUncheckedIndexedAccess": null, + "noEmitOnError": null, + "noUnusedLocals": null, + "noUnusedParameters": null, + "noResolve": null, + "noImplicitOverride": null, + "noUncheckedSideEffectImports": null, + "out": "", + "outDir": "", + "outFile": "", + "paths": null, + "preserveConstEnums": null, + "preserveSymlinks": null, + "project": "", + "resolveJsonModule": null, + "resolvePackageJsonExports": null, + "resolvePackageJsonImports": null, + "removeComments": null, + "rewriteRelativeImportExtensions": null, + "reactNamespace": "", + "rootDir": "", + "rootDirs": null, + "skipLibCheck": null, + "strict": null, + "strictBindCallApply": null, + "strictBuiltinIteratorReturn": null, + "strictFunctionTypes": null, + "strictNullChecks": null, + "strictPropertyInitialization": null, + "stripInternal": null, + "skipDefaultLibCheck": null, + "sourceMap": null, + "sourceRoot": "", + "suppressOutputPathCheck": null, + "target": 0, + "traceResolution": null, + "tsBuildInfoFile": "", + "typeRoots": null, + "types": null, + "useDefineForClassFields": null, + "useUnknownInCatchVariables": null, + "verbatimModuleSyntax": null, + "maxNodeModuleJsDepth": null, + "configFilePath": "", + "noDtsResolution": null, + "pathsBasePath": "", + "diagnostics": null, + "extendedDiagnostics": null, + "generateCpuProfile": "", + "generateTrace": "", + "listEmittedFiles": null, + "listFiles": null, + "explainFiles": null, + "listFilesOnly": null, + "noEmitForJsFiles": null, + "preserveWatchOutput": null, + "pretty": null, + "version": null, + "watch": null, + "showConfig": null, + "tscBuild": null +} +Output:: +error TS6266: Option 'help' can only be specified on command line. + diff --git a/testdata/baselines/reference/tsc/commandLine/show-help-with-ExitStatus.DiagnosticsPresent_OutputsSkipped-when-host-can't-provide-terminal-width.js b/testdata/baselines/reference/tsc/commandLine/show-help-with-ExitStatus.DiagnosticsPresent_OutputsSkipped-when-host-can't-provide-terminal-width.js new file mode 100644 index 0000000000..1dd14c5c24 --- /dev/null +++ b/testdata/baselines/reference/tsc/commandLine/show-help-with-ExitStatus.DiagnosticsPresent_OutputsSkipped-when-host-can't-provide-terminal-width.js @@ -0,0 +1,134 @@ + +currentDirectory::/home/src/workspaces/project +useCaseSensitiveFileNames::true +Input:: + + +ExitStatus:: 1 + +CompilerOptions::{ + "allowJs": null, + "allowArbitraryExtensions": null, + "allowSyntheticDefaultImports": null, + "allowImportingTsExtensions": null, + "allowNonTsExtensions": null, + "allowUmdGlobalAccess": null, + "allowUnreachableCode": null, + "allowUnusedLabels": null, + "assumeChangesOnlyAffectDirectDependencies": null, + "alwaysStrict": null, + "baseUrl": "", + "build": null, + "checkJs": null, + "customConditions": null, + "composite": null, + "emitDeclarationOnly": null, + "emitBOM": null, + "emitDecoratorMetadata": null, + "downlevelIteration": null, + "declaration": null, + "declarationDir": "", + "declarationMap": null, + "disableSizeLimit": null, + "disableSourceOfProjectReferenceRedirect": null, + "disableSolutionSearching": null, + "disableReferencedProjectLoad": null, + "esModuleInterop": null, + "exactOptionalPropertyTypes": null, + "experimentalDecorators": null, + "forceConsistentCasingInFileNames": null, + "isolatedModules": null, + "isolatedDeclarations": null, + "ignoreDeprecations": "", + "importHelpers": null, + "inlineSourceMap": null, + "inlineSources": null, + "init": null, + "incremental": null, + "jsx": 0, + "jsxFactory": "", + "jsxFragmentFactory": "", + "jsxImportSource": "", + "keyofStringsOnly": null, + "lib": null, + "locale": "", + "mapRoot": "", + "module": 0, + "moduleResolution": 0, + "moduleSuffixes": null, + "moduleDetectionKind": 0, + "newLine": 0, + "noEmit": null, + "noCheck": null, + "noErrorTruncation": null, + "noFallthroughCasesInSwitch": null, + "noImplicitAny": null, + "noImplicitThis": null, + "noImplicitReturns": null, + "noEmitHelpers": null, + "noLib": null, + "noPropertyAccessFromIndexSignature": null, + "noUncheckedIndexedAccess": null, + "noEmitOnError": null, + "noUnusedLocals": null, + "noUnusedParameters": null, + "noResolve": null, + "noImplicitOverride": null, + "noUncheckedSideEffectImports": null, + "out": "", + "outDir": "", + "outFile": "", + "paths": null, + "preserveConstEnums": null, + "preserveSymlinks": null, + "project": "", + "resolveJsonModule": null, + "resolvePackageJsonExports": null, + "resolvePackageJsonImports": null, + "removeComments": null, + "rewriteRelativeImportExtensions": null, + "reactNamespace": "", + "rootDir": "", + "rootDirs": null, + "skipLibCheck": null, + "strict": null, + "strictBindCallApply": null, + "strictBuiltinIteratorReturn": null, + "strictFunctionTypes": null, + "strictNullChecks": null, + "strictPropertyInitialization": null, + "stripInternal": null, + "skipDefaultLibCheck": null, + "sourceMap": null, + "sourceRoot": "", + "suppressOutputPathCheck": null, + "target": 0, + "traceResolution": null, + "tsBuildInfoFile": "", + "typeRoots": null, + "types": null, + "useDefineForClassFields": null, + "useUnknownInCatchVariables": null, + "verbatimModuleSyntax": null, + "maxNodeModuleJsDepth": null, + "configFilePath": "", + "noDtsResolution": null, + "pathsBasePath": "", + "diagnostics": null, + "extendedDiagnostics": null, + "generateCpuProfile": "", + "generateTrace": "", + "listEmittedFiles": null, + "listFiles": null, + "explainFiles": null, + "listFilesOnly": null, + "noEmitForJsFiles": null, + "preserveWatchOutput": null, + "pretty": null, + "version": null, + "watch": null, + "showConfig": null, + "tscBuild": null +} +Output:: + diff --git a/testdata/baselines/reference/tsc/commandLine/show-help-with-ExitStatus.DiagnosticsPresent_OutputsSkipped.js b/testdata/baselines/reference/tsc/commandLine/show-help-with-ExitStatus.DiagnosticsPresent_OutputsSkipped.js new file mode 100644 index 0000000000..1dd14c5c24 --- /dev/null +++ b/testdata/baselines/reference/tsc/commandLine/show-help-with-ExitStatus.DiagnosticsPresent_OutputsSkipped.js @@ -0,0 +1,134 @@ + +currentDirectory::/home/src/workspaces/project +useCaseSensitiveFileNames::true +Input:: + + +ExitStatus:: 1 + +CompilerOptions::{ + "allowJs": null, + "allowArbitraryExtensions": null, + "allowSyntheticDefaultImports": null, + "allowImportingTsExtensions": null, + "allowNonTsExtensions": null, + "allowUmdGlobalAccess": null, + "allowUnreachableCode": null, + "allowUnusedLabels": null, + "assumeChangesOnlyAffectDirectDependencies": null, + "alwaysStrict": null, + "baseUrl": "", + "build": null, + "checkJs": null, + "customConditions": null, + "composite": null, + "emitDeclarationOnly": null, + "emitBOM": null, + "emitDecoratorMetadata": null, + "downlevelIteration": null, + "declaration": null, + "declarationDir": "", + "declarationMap": null, + "disableSizeLimit": null, + "disableSourceOfProjectReferenceRedirect": null, + "disableSolutionSearching": null, + "disableReferencedProjectLoad": null, + "esModuleInterop": null, + "exactOptionalPropertyTypes": null, + "experimentalDecorators": null, + "forceConsistentCasingInFileNames": null, + "isolatedModules": null, + "isolatedDeclarations": null, + "ignoreDeprecations": "", + "importHelpers": null, + "inlineSourceMap": null, + "inlineSources": null, + "init": null, + "incremental": null, + "jsx": 0, + "jsxFactory": "", + "jsxFragmentFactory": "", + "jsxImportSource": "", + "keyofStringsOnly": null, + "lib": null, + "locale": "", + "mapRoot": "", + "module": 0, + "moduleResolution": 0, + "moduleSuffixes": null, + "moduleDetectionKind": 0, + "newLine": 0, + "noEmit": null, + "noCheck": null, + "noErrorTruncation": null, + "noFallthroughCasesInSwitch": null, + "noImplicitAny": null, + "noImplicitThis": null, + "noImplicitReturns": null, + "noEmitHelpers": null, + "noLib": null, + "noPropertyAccessFromIndexSignature": null, + "noUncheckedIndexedAccess": null, + "noEmitOnError": null, + "noUnusedLocals": null, + "noUnusedParameters": null, + "noResolve": null, + "noImplicitOverride": null, + "noUncheckedSideEffectImports": null, + "out": "", + "outDir": "", + "outFile": "", + "paths": null, + "preserveConstEnums": null, + "preserveSymlinks": null, + "project": "", + "resolveJsonModule": null, + "resolvePackageJsonExports": null, + "resolvePackageJsonImports": null, + "removeComments": null, + "rewriteRelativeImportExtensions": null, + "reactNamespace": "", + "rootDir": "", + "rootDirs": null, + "skipLibCheck": null, + "strict": null, + "strictBindCallApply": null, + "strictBuiltinIteratorReturn": null, + "strictFunctionTypes": null, + "strictNullChecks": null, + "strictPropertyInitialization": null, + "stripInternal": null, + "skipDefaultLibCheck": null, + "sourceMap": null, + "sourceRoot": "", + "suppressOutputPathCheck": null, + "target": 0, + "traceResolution": null, + "tsBuildInfoFile": "", + "typeRoots": null, + "types": null, + "useDefineForClassFields": null, + "useUnknownInCatchVariables": null, + "verbatimModuleSyntax": null, + "maxNodeModuleJsDepth": null, + "configFilePath": "", + "noDtsResolution": null, + "pathsBasePath": "", + "diagnostics": null, + "extendedDiagnostics": null, + "generateCpuProfile": "", + "generateTrace": "", + "listEmittedFiles": null, + "listFiles": null, + "explainFiles": null, + "listFilesOnly": null, + "noEmitForJsFiles": null, + "preserveWatchOutput": null, + "pretty": null, + "version": null, + "watch": null, + "showConfig": null, + "tscBuild": null +} +Output:: + diff --git a/testdata/baselines/reference/tsc/commandLine/when-build-not-first-argument.js b/testdata/baselines/reference/tsc/commandLine/when-build-not-first-argument.js new file mode 100644 index 0000000000..0b0de660aa --- /dev/null +++ b/testdata/baselines/reference/tsc/commandLine/when-build-not-first-argument.js @@ -0,0 +1,137 @@ + +currentDirectory::/home/src/workspaces/project +useCaseSensitiveFileNames::true +Input::--verbose --build + + +ExitStatus:: 1 + +CompilerOptions::{ + "allowJs": null, + "allowArbitraryExtensions": null, + "allowSyntheticDefaultImports": null, + "allowImportingTsExtensions": null, + "allowNonTsExtensions": null, + "allowUmdGlobalAccess": null, + "allowUnreachableCode": null, + "allowUnusedLabels": null, + "assumeChangesOnlyAffectDirectDependencies": null, + "alwaysStrict": null, + "baseUrl": "", + "build": null, + "checkJs": null, + "customConditions": null, + "composite": null, + "emitDeclarationOnly": null, + "emitBOM": null, + "emitDecoratorMetadata": null, + "downlevelIteration": null, + "declaration": null, + "declarationDir": "", + "declarationMap": null, + "disableSizeLimit": null, + "disableSourceOfProjectReferenceRedirect": null, + "disableSolutionSearching": null, + "disableReferencedProjectLoad": null, + "esModuleInterop": null, + "exactOptionalPropertyTypes": null, + "experimentalDecorators": null, + "forceConsistentCasingInFileNames": null, + "isolatedModules": null, + "isolatedDeclarations": null, + "ignoreDeprecations": "", + "importHelpers": null, + "inlineSourceMap": null, + "inlineSources": null, + "init": null, + "incremental": null, + "jsx": 0, + "jsxFactory": "", + "jsxFragmentFactory": "", + "jsxImportSource": "", + "keyofStringsOnly": null, + "lib": null, + "locale": "", + "mapRoot": "", + "module": 0, + "moduleResolution": 0, + "moduleSuffixes": null, + "moduleDetectionKind": 0, + "newLine": 0, + "noEmit": null, + "noCheck": null, + "noErrorTruncation": null, + "noFallthroughCasesInSwitch": null, + "noImplicitAny": null, + "noImplicitThis": null, + "noImplicitReturns": null, + "noEmitHelpers": null, + "noLib": null, + "noPropertyAccessFromIndexSignature": null, + "noUncheckedIndexedAccess": null, + "noEmitOnError": null, + "noUnusedLocals": null, + "noUnusedParameters": null, + "noResolve": null, + "noImplicitOverride": null, + "noUncheckedSideEffectImports": null, + "out": "", + "outDir": "", + "outFile": "", + "paths": null, + "preserveConstEnums": null, + "preserveSymlinks": null, + "project": "", + "resolveJsonModule": null, + "resolvePackageJsonExports": null, + "resolvePackageJsonImports": null, + "removeComments": null, + "rewriteRelativeImportExtensions": null, + "reactNamespace": "", + "rootDir": "", + "rootDirs": null, + "skipLibCheck": null, + "strict": null, + "strictBindCallApply": null, + "strictBuiltinIteratorReturn": null, + "strictFunctionTypes": null, + "strictNullChecks": null, + "strictPropertyInitialization": null, + "stripInternal": null, + "skipDefaultLibCheck": null, + "sourceMap": null, + "sourceRoot": "", + "suppressOutputPathCheck": null, + "target": 0, + "traceResolution": null, + "tsBuildInfoFile": "", + "typeRoots": null, + "types": null, + "useDefineForClassFields": null, + "useUnknownInCatchVariables": null, + "verbatimModuleSyntax": null, + "maxNodeModuleJsDepth": null, + "configFilePath": "", + "noDtsResolution": null, + "pathsBasePath": "", + "diagnostics": null, + "extendedDiagnostics": null, + "generateCpuProfile": "", + "generateTrace": "", + "listEmittedFiles": null, + "listFiles": null, + "explainFiles": null, + "listFilesOnly": null, + "noEmitForJsFiles": null, + "preserveWatchOutput": null, + "pretty": null, + "version": null, + "watch": null, + "showConfig": null, + "tscBuild": null +} +Output:: +error TS5093: Compiler option '--verbose' may only be used with '--build'. + +error TS6369: Option '--build' must be the first command line argument. + diff --git a/testdata/baselines/reference/tsc/noEmit/when-project-has-strict-true.js b/testdata/baselines/reference/tsc/noEmit/when-project-has-strict-true.js new file mode 100644 index 0000000000..0326c5daa6 --- /dev/null +++ b/testdata/baselines/reference/tsc/noEmit/when-project-has-strict-true.js @@ -0,0 +1,141 @@ + +currentDirectory::/home/src/workspaces/project +useCaseSensitiveFileNames::true +Input::--noEmit + + +ExitStatus:: 7 + +CompilerOptions::{ + "allowJs": null, + "allowArbitraryExtensions": null, + "allowSyntheticDefaultImports": null, + "allowImportingTsExtensions": null, + "allowNonTsExtensions": null, + "allowUmdGlobalAccess": null, + "allowUnreachableCode": null, + "allowUnusedLabels": null, + "assumeChangesOnlyAffectDirectDependencies": null, + "alwaysStrict": null, + "baseUrl": "", + "build": null, + "checkJs": null, + "customConditions": null, + "composite": null, + "emitDeclarationOnly": null, + "emitBOM": null, + "emitDecoratorMetadata": null, + "downlevelIteration": null, + "declaration": null, + "declarationDir": "", + "declarationMap": null, + "disableSizeLimit": null, + "disableSourceOfProjectReferenceRedirect": null, + "disableSolutionSearching": null, + "disableReferencedProjectLoad": null, + "esModuleInterop": null, + "exactOptionalPropertyTypes": null, + "experimentalDecorators": null, + "forceConsistentCasingInFileNames": null, + "isolatedModules": null, + "isolatedDeclarations": null, + "ignoreDeprecations": "", + "importHelpers": null, + "inlineSourceMap": null, + "inlineSources": null, + "init": null, + "incremental": null, + "jsx": 0, + "jsxFactory": "", + "jsxFragmentFactory": "", + "jsxImportSource": "", + "keyofStringsOnly": null, + "lib": null, + "locale": "", + "mapRoot": "", + "module": 0, + "moduleResolution": 0, + "moduleSuffixes": null, + "moduleDetectionKind": 0, + "newLine": 0, + "noEmit": true, + "noCheck": null, + "noErrorTruncation": null, + "noFallthroughCasesInSwitch": null, + "noImplicitAny": null, + "noImplicitThis": null, + "noImplicitReturns": null, + "noEmitHelpers": null, + "noLib": null, + "noPropertyAccessFromIndexSignature": null, + "noUncheckedIndexedAccess": null, + "noEmitOnError": null, + "noUnusedLocals": null, + "noUnusedParameters": null, + "noResolve": null, + "noImplicitOverride": null, + "noUncheckedSideEffectImports": null, + "out": "", + "outDir": "", + "outFile": "", + "paths": null, + "preserveConstEnums": null, + "preserveSymlinks": null, + "project": "", + "resolveJsonModule": null, + "resolvePackageJsonExports": null, + "resolvePackageJsonImports": null, + "removeComments": null, + "rewriteRelativeImportExtensions": null, + "reactNamespace": "", + "rootDir": "", + "rootDirs": null, + "skipLibCheck": null, + "strict": null, + "strictBindCallApply": null, + "strictBuiltinIteratorReturn": null, + "strictFunctionTypes": null, + "strictNullChecks": null, + "strictPropertyInitialization": null, + "stripInternal": null, + "skipDefaultLibCheck": null, + "sourceMap": null, + "sourceRoot": "", + "suppressOutputPathCheck": null, + "target": 0, + "traceResolution": null, + "tsBuildInfoFile": "", + "typeRoots": null, + "types": null, + "useDefineForClassFields": null, + "useUnknownInCatchVariables": null, + "verbatimModuleSyntax": null, + "maxNodeModuleJsDepth": null, + "configFilePath": "", + "noDtsResolution": null, + "pathsBasePath": "", + "diagnostics": null, + "extendedDiagnostics": null, + "generateCpuProfile": "", + "generateTrace": "", + "listEmittedFiles": null, + "listFiles": null, + "explainFiles": null, + "listFilesOnly": null, + "noEmitForJsFiles": null, + "preserveWatchOutput": null, + "pretty": null, + "version": null, + "watch": null, + "showConfig": null, + "tscBuild": null +} +Output:: +//// [/home/src/workspaces/project/class1.ts]\nexport class class1 {} +//// [/home/src/workspaces/project/tsconfig.json]\n{ + "compilerOptions": { + "incremental": true, + "strict": true, + }, +} + diff --git a/testdata/baselines/reference/tsc/projectReferences/when-project-references-composite-project-with-noEmit.js b/testdata/baselines/reference/tsc/projectReferences/when-project-references-composite-project-with-noEmit.js new file mode 100644 index 0000000000..41ef0c4f11 --- /dev/null +++ b/testdata/baselines/reference/tsc/projectReferences/when-project-references-composite-project-with-noEmit.js @@ -0,0 +1,148 @@ + +currentDirectory::/home/src/workspaces/solution +useCaseSensitiveFileNames::true +Input::--p project + + +ExitStatus:: 0 + +CompilerOptions::{ + "allowJs": null, + "allowArbitraryExtensions": null, + "allowSyntheticDefaultImports": null, + "allowImportingTsExtensions": null, + "allowNonTsExtensions": null, + "allowUmdGlobalAccess": null, + "allowUnreachableCode": null, + "allowUnusedLabels": null, + "assumeChangesOnlyAffectDirectDependencies": null, + "alwaysStrict": null, + "baseUrl": "", + "build": null, + "checkJs": null, + "customConditions": null, + "composite": null, + "emitDeclarationOnly": null, + "emitBOM": null, + "emitDecoratorMetadata": null, + "downlevelIteration": null, + "declaration": null, + "declarationDir": "", + "declarationMap": null, + "disableSizeLimit": null, + "disableSourceOfProjectReferenceRedirect": null, + "disableSolutionSearching": null, + "disableReferencedProjectLoad": null, + "esModuleInterop": null, + "exactOptionalPropertyTypes": null, + "experimentalDecorators": null, + "forceConsistentCasingInFileNames": null, + "isolatedModules": null, + "isolatedDeclarations": null, + "ignoreDeprecations": "", + "importHelpers": null, + "inlineSourceMap": null, + "inlineSources": null, + "init": null, + "incremental": null, + "jsx": 0, + "jsxFactory": "", + "jsxFragmentFactory": "", + "jsxImportSource": "", + "keyofStringsOnly": null, + "lib": null, + "locale": "", + "mapRoot": "", + "module": 0, + "moduleResolution": 0, + "moduleSuffixes": null, + "moduleDetectionKind": 0, + "newLine": 0, + "noEmit": null, + "noCheck": null, + "noErrorTruncation": null, + "noFallthroughCasesInSwitch": null, + "noImplicitAny": null, + "noImplicitThis": null, + "noImplicitReturns": null, + "noEmitHelpers": null, + "noLib": null, + "noPropertyAccessFromIndexSignature": null, + "noUncheckedIndexedAccess": null, + "noEmitOnError": null, + "noUnusedLocals": null, + "noUnusedParameters": null, + "noResolve": null, + "noImplicitOverride": null, + "noUncheckedSideEffectImports": null, + "out": "", + "outDir": "", + "outFile": "", + "paths": null, + "preserveConstEnums": null, + "preserveSymlinks": null, + "project": "/home/src/workspaces/solution/project", + "resolveJsonModule": null, + "resolvePackageJsonExports": null, + "resolvePackageJsonImports": null, + "removeComments": null, + "rewriteRelativeImportExtensions": null, + "reactNamespace": "", + "rootDir": "", + "rootDirs": null, + "skipLibCheck": null, + "strict": null, + "strictBindCallApply": null, + "strictBuiltinIteratorReturn": null, + "strictFunctionTypes": null, + "strictNullChecks": null, + "strictPropertyInitialization": null, + "stripInternal": null, + "skipDefaultLibCheck": null, + "sourceMap": null, + "sourceRoot": "", + "suppressOutputPathCheck": null, + "target": 0, + "traceResolution": null, + "tsBuildInfoFile": "", + "typeRoots": null, + "types": null, + "useDefineForClassFields": null, + "useUnknownInCatchVariables": null, + "verbatimModuleSyntax": null, + "maxNodeModuleJsDepth": null, + "configFilePath": "", + "noDtsResolution": null, + "pathsBasePath": "", + "diagnostics": null, + "extendedDiagnostics": null, + "generateCpuProfile": "", + "generateTrace": "", + "listEmittedFiles": null, + "listFiles": null, + "explainFiles": null, + "listFilesOnly": null, + "noEmitForJsFiles": null, + "preserveWatchOutput": null, + "pretty": null, + "version": null, + "watch": null, + "showConfig": null, + "tscBuild": null +} +Output:: +//// [/home/src/workspaces/solution/project/index.ts]\nimport { x } from "../utils"; +//// [/home/src/workspaces/solution/project/tsconfig.json]\n{ + "references": [ + { "path": "../utils" }, + ], + }), +}, +//// [/home/src/workspaces/solution/src/utils/index.ts]\nexport const x = 10; +//// [/home/src/workspaces/solution/src/utils/tsconfig.json]\n{ + "compilerOptions": { + "composite": true, + "noEmit": true, + }, +}) +