320 lines
12 KiB
Go
320 lines
12 KiB
Go
package execute
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/compiler"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/execute/build"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/execute/incremental"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/execute/tsc"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/format"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsonutil"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/parser"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/pprof"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
|
)
|
|
|
|
func CommandLine(sys tsc.System, commandLineArgs []string, testing tsc.CommandLineTesting) tsc.CommandLineResult {
|
|
if len(commandLineArgs) > 0 {
|
|
// !!! build mode
|
|
switch strings.ToLower(commandLineArgs[0]) {
|
|
case "-b", "--b", "-build", "--build":
|
|
return tscBuildCompilation(sys, tsoptions.ParseBuildCommandLine(commandLineArgs, sys), testing)
|
|
// case "-f":
|
|
// return fmtMain(sys, commandLineArgs[1], commandLineArgs[1])
|
|
}
|
|
}
|
|
|
|
return tscCompilation(sys, tsoptions.ParseCommandLine(commandLineArgs, sys), testing)
|
|
}
|
|
|
|
func fmtMain(sys tsc.System, input, output string) tsc.ExitStatus {
|
|
ctx := format.WithFormatCodeSettings(context.Background(), format.GetDefaultFormatCodeSettings("\n"), "\n")
|
|
input = string(tspath.ToPath(input, sys.GetCurrentDirectory(), sys.FS().UseCaseSensitiveFileNames()))
|
|
output = string(tspath.ToPath(output, sys.GetCurrentDirectory(), sys.FS().UseCaseSensitiveFileNames()))
|
|
fileContent, ok := sys.FS().ReadFile(input)
|
|
if !ok {
|
|
fmt.Fprintln(sys.Writer(), "File not found:", input)
|
|
return tsc.ExitStatusNotImplemented
|
|
}
|
|
text := fileContent
|
|
pathified := tspath.ToPath(input, sys.GetCurrentDirectory(), true)
|
|
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
|
|
FileName: string(pathified),
|
|
Path: pathified,
|
|
JSDocParsingMode: ast.JSDocParsingModeParseAll,
|
|
}, text, core.GetScriptKindFromFileName(string(pathified)))
|
|
edits := format.FormatDocument(ctx, sourceFile)
|
|
newText := core.ApplyBulkEdits(text, edits)
|
|
|
|
if err := sys.FS().WriteFile(output, newText, false); err != nil {
|
|
fmt.Fprintln(sys.Writer(), err.Error())
|
|
return tsc.ExitStatusNotImplemented
|
|
}
|
|
return tsc.ExitStatusSuccess
|
|
}
|
|
|
|
func tscBuildCompilation(sys tsc.System, buildCommand *tsoptions.ParsedBuildCommandLine, testing tsc.CommandLineTesting) tsc.CommandLineResult {
|
|
reportDiagnostic := tsc.CreateDiagnosticReporter(sys, sys.Writer(), buildCommand.CompilerOptions)
|
|
|
|
// if (buildOptions.locale) {
|
|
// validateLocaleAndSetLanguage(buildOptions.locale, sys, errors);
|
|
// }
|
|
|
|
if len(buildCommand.Errors) > 0 {
|
|
for _, err := range buildCommand.Errors {
|
|
reportDiagnostic(err)
|
|
}
|
|
return tsc.CommandLineResult{Status: tsc.ExitStatusDiagnosticsPresent_OutputsSkipped}
|
|
}
|
|
|
|
if pprofDir := buildCommand.CompilerOptions.PprofDir; pprofDir != "" {
|
|
// !!! stderr?
|
|
profileSession := pprof.BeginProfiling(pprofDir, sys.Writer())
|
|
defer profileSession.Stop()
|
|
}
|
|
|
|
if buildCommand.CompilerOptions.Help.IsTrue() {
|
|
tsc.PrintVersion(sys)
|
|
tsc.PrintBuildHelp(sys, tsoptions.BuildOpts)
|
|
return tsc.CommandLineResult{Status: tsc.ExitStatusSuccess}
|
|
}
|
|
|
|
orchestrator := build.NewOrchestrator(build.Options{
|
|
Sys: sys,
|
|
Command: buildCommand,
|
|
Testing: testing,
|
|
})
|
|
return orchestrator.Start()
|
|
}
|
|
|
|
func tscCompilation(sys tsc.System, commandLine *tsoptions.ParsedCommandLine, testing tsc.CommandLineTesting) tsc.CommandLineResult {
|
|
configFileName := ""
|
|
reportDiagnostic := tsc.CreateDiagnosticReporter(sys, sys.Writer(), commandLine.CompilerOptions())
|
|
// if commandLine.Options().Locale != nil
|
|
|
|
if len(commandLine.Errors) > 0 {
|
|
for _, e := range commandLine.Errors {
|
|
reportDiagnostic(e)
|
|
}
|
|
return tsc.CommandLineResult{Status: tsc.ExitStatusDiagnosticsPresent_OutputsSkipped}
|
|
}
|
|
|
|
if pprofDir := commandLine.CompilerOptions().PprofDir; pprofDir != "" {
|
|
// !!! stderr?
|
|
profileSession := pprof.BeginProfiling(pprofDir, sys.Writer())
|
|
defer profileSession.Stop()
|
|
}
|
|
|
|
if commandLine.CompilerOptions().Init.IsTrue() {
|
|
return tsc.CommandLineResult{Status: tsc.ExitStatusNotImplemented}
|
|
}
|
|
|
|
if commandLine.CompilerOptions().Version.IsTrue() {
|
|
tsc.PrintVersion(sys)
|
|
return tsc.CommandLineResult{Status: tsc.ExitStatusSuccess}
|
|
}
|
|
|
|
if commandLine.CompilerOptions().Help.IsTrue() || commandLine.CompilerOptions().All.IsTrue() {
|
|
tsc.PrintHelp(sys, commandLine)
|
|
return tsc.CommandLineResult{Status: tsc.ExitStatusSuccess}
|
|
}
|
|
|
|
if commandLine.CompilerOptions().Watch.IsTrue() && commandLine.CompilerOptions().ListFilesOnly.IsTrue() {
|
|
reportDiagnostic(ast.NewCompilerDiagnostic(diagnostics.Options_0_and_1_cannot_be_combined, "watch", "listFilesOnly"))
|
|
return tsc.CommandLineResult{Status: tsc.ExitStatusDiagnosticsPresent_OutputsSkipped}
|
|
}
|
|
|
|
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 tsc.CommandLineResult{Status: tsc.ExitStatusDiagnosticsPresent_OutputsSkipped}
|
|
}
|
|
|
|
fileOrDirectory := tspath.NormalizePath(commandLine.CompilerOptions().Project)
|
|
if 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 tsc.CommandLineResult{Status: tsc.ExitStatusDiagnosticsPresent_OutputsSkipped}
|
|
}
|
|
} else {
|
|
configFileName = fileOrDirectory
|
|
if !sys.FS().FileExists(configFileName) {
|
|
reportDiagnostic(ast.NewCompilerDiagnostic(diagnostics.The_specified_path_does_not_exist_Colon_0, fileOrDirectory))
|
|
return tsc.CommandLineResult{Status: tsc.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 {
|
|
tsc.PrintVersion(sys)
|
|
tsc.PrintHelp(sys, commandLine)
|
|
}
|
|
return tsc.CommandLineResult{Status: tsc.ExitStatusDiagnosticsPresent_OutputsSkipped}
|
|
}
|
|
|
|
// !!! convert to options with absolute paths is usually done here, but for ease of implementation, it's done in `tsoptions.ParseCommandLine()`
|
|
compilerOptionsFromCommandLine := commandLine.CompilerOptions()
|
|
configForCompilation := commandLine
|
|
extendedConfigCache := &tsc.ExtendedConfigCache{}
|
|
var compileTimes tsc.CompileTimes
|
|
if configFileName != "" {
|
|
configStart := sys.Now()
|
|
configParseResult, errors := tsoptions.GetParsedCommandLineOfConfigFile(configFileName, compilerOptionsFromCommandLine, sys, extendedConfigCache)
|
|
compileTimes.ConfigTime = sys.Now().Sub(configStart)
|
|
if len(errors) != 0 {
|
|
// these are unrecoverable errors--exit to report them as diagnostics
|
|
for _, e := range errors {
|
|
reportDiagnostic(e)
|
|
}
|
|
return tsc.CommandLineResult{Status: tsc.ExitStatusDiagnosticsPresent_OutputsGenerated}
|
|
}
|
|
configForCompilation = configParseResult
|
|
// Updater to reflect pretty
|
|
reportDiagnostic = tsc.CreateDiagnosticReporter(sys, sys.Writer(), commandLine.CompilerOptions())
|
|
}
|
|
|
|
reportErrorSummary := tsc.CreateReportErrorSummary(sys, configForCompilation.CompilerOptions())
|
|
if compilerOptionsFromCommandLine.ShowConfig.IsTrue() {
|
|
showConfig(sys, configForCompilation.CompilerOptions())
|
|
return tsc.CommandLineResult{Status: tsc.ExitStatusSuccess}
|
|
}
|
|
if configForCompilation.CompilerOptions().Watch.IsTrue() {
|
|
watcher := createWatcher(sys, configForCompilation, reportDiagnostic, reportErrorSummary, testing)
|
|
watcher.start()
|
|
return tsc.CommandLineResult{Status: tsc.ExitStatusSuccess, Watcher: watcher}
|
|
} else if configForCompilation.CompilerOptions().IsIncremental() {
|
|
return performIncrementalCompilation(
|
|
sys,
|
|
configForCompilation,
|
|
reportDiagnostic,
|
|
reportErrorSummary,
|
|
extendedConfigCache,
|
|
&compileTimes,
|
|
testing,
|
|
)
|
|
}
|
|
return performCompilation(
|
|
sys,
|
|
configForCompilation,
|
|
reportDiagnostic,
|
|
reportErrorSummary,
|
|
extendedConfigCache,
|
|
&compileTimes,
|
|
testing,
|
|
)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func getTraceFromSys(sys tsc.System, testing tsc.CommandLineTesting) func(msg string) {
|
|
return tsc.GetTraceWithWriterFromSys(sys.Writer(), testing)
|
|
}
|
|
|
|
func performIncrementalCompilation(
|
|
sys tsc.System,
|
|
config *tsoptions.ParsedCommandLine,
|
|
reportDiagnostic tsc.DiagnosticReporter,
|
|
reportErrorSummary tsc.DiagnosticsReporter,
|
|
extendedConfigCache tsoptions.ExtendedConfigCache,
|
|
compileTimes *tsc.CompileTimes,
|
|
testing tsc.CommandLineTesting,
|
|
) tsc.CommandLineResult {
|
|
host := compiler.NewCachedFSCompilerHost(sys.GetCurrentDirectory(), sys.FS(), sys.DefaultLibraryPath(), extendedConfigCache, getTraceFromSys(sys, testing))
|
|
buildInfoReadStart := sys.Now()
|
|
oldProgram := incremental.ReadBuildInfoProgram(config, incremental.NewBuildInfoReader(host), host)
|
|
compileTimes.BuildInfoReadTime = sys.Now().Sub(buildInfoReadStart)
|
|
// todo: cache, statistics, tracing
|
|
parseStart := sys.Now()
|
|
program := compiler.NewProgram(compiler.ProgramOptions{
|
|
Config: config,
|
|
Host: host,
|
|
JSDocParsingMode: ast.JSDocParsingModeParseForTypeErrors,
|
|
})
|
|
compileTimes.ParseTime = sys.Now().Sub(parseStart)
|
|
changesComputeStart := sys.Now()
|
|
incrementalProgram := incremental.NewProgram(program, oldProgram, incremental.CreateHost(host), testing != nil)
|
|
compileTimes.ChangesComputeTime = sys.Now().Sub(changesComputeStart)
|
|
result, _ := tsc.EmitAndReportStatistics(tsc.EmitInput{
|
|
Sys: sys,
|
|
ProgramLike: incrementalProgram,
|
|
Program: incrementalProgram.GetProgram(),
|
|
Config: config,
|
|
ReportDiagnostic: reportDiagnostic,
|
|
ReportErrorSummary: reportErrorSummary,
|
|
Writer: sys.Writer(),
|
|
CompileTimes: compileTimes,
|
|
Testing: testing,
|
|
})
|
|
if testing != nil {
|
|
testing.OnProgram(incrementalProgram)
|
|
}
|
|
return tsc.CommandLineResult{
|
|
Status: result.Status,
|
|
}
|
|
}
|
|
|
|
func performCompilation(
|
|
sys tsc.System,
|
|
config *tsoptions.ParsedCommandLine,
|
|
reportDiagnostic tsc.DiagnosticReporter,
|
|
reportErrorSummary tsc.DiagnosticsReporter,
|
|
extendedConfigCache tsoptions.ExtendedConfigCache,
|
|
compileTimes *tsc.CompileTimes,
|
|
testing tsc.CommandLineTesting,
|
|
) tsc.CommandLineResult {
|
|
host := compiler.NewCachedFSCompilerHost(sys.GetCurrentDirectory(), sys.FS(), sys.DefaultLibraryPath(), extendedConfigCache, getTraceFromSys(sys, testing))
|
|
// todo: cache, statistics, tracing
|
|
parseStart := sys.Now()
|
|
program := compiler.NewProgram(compiler.ProgramOptions{
|
|
Config: config,
|
|
Host: host,
|
|
JSDocParsingMode: ast.JSDocParsingModeParseForTypeErrors,
|
|
})
|
|
compileTimes.ParseTime = sys.Now().Sub(parseStart)
|
|
result, _ := tsc.EmitAndReportStatistics(tsc.EmitInput{
|
|
Sys: sys,
|
|
ProgramLike: program,
|
|
Program: program,
|
|
Config: config,
|
|
ReportDiagnostic: reportDiagnostic,
|
|
ReportErrorSummary: reportErrorSummary,
|
|
Writer: sys.Writer(),
|
|
CompileTimes: compileTimes,
|
|
Testing: testing,
|
|
})
|
|
return tsc.CommandLineResult{
|
|
Status: result.Status,
|
|
}
|
|
}
|
|
|
|
func showConfig(sys tsc.System, config *core.CompilerOptions) {
|
|
// !!!
|
|
_ = jsonutil.MarshalIndentWrite(sys.Writer(), config, "", " ")
|
|
}
|