vendor tsgo
This commit is contained in:
423
tools/tsgo/internal/tsoptions/parsedcommandline.go
Normal file
423
tools/tsgo/internal/tsoptions/parsedcommandline.go
Normal file
@@ -0,0 +1,423 @@
|
||||
package tsoptions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"iter"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/diagnostics"
|
||||
"github.com/microsoft/typescript-go/internal/glob"
|
||||
"github.com/microsoft/typescript-go/internal/locale"
|
||||
"github.com/microsoft/typescript-go/internal/module"
|
||||
"github.com/microsoft/typescript-go/internal/outputpaths"
|
||||
"github.com/microsoft/typescript-go/internal/tspath"
|
||||
"github.com/microsoft/typescript-go/internal/vfs"
|
||||
"github.com/microsoft/typescript-go/internal/vfs/vfsmatch"
|
||||
)
|
||||
|
||||
const (
|
||||
fileGlobPattern = "*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,json}"
|
||||
recursiveFileGlobPattern = "**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,json}"
|
||||
)
|
||||
|
||||
type ParsedCommandLine struct {
|
||||
ParsedConfig *core.ParsedOptions `json:"parsedConfig"`
|
||||
|
||||
ConfigFile *TsConfigSourceFile `json:"configFile"` // TsConfigSourceFile, used in Program and ExecuteCommandLine
|
||||
Errors []*ast.Diagnostic `json:"errors"`
|
||||
Raw any `json:"raw"`
|
||||
CompileOnSave *bool `json:"compileOnSave"`
|
||||
|
||||
comparePathsOptions tspath.ComparePathsOptions
|
||||
wildcardDirectoriesOnce sync.Once
|
||||
wildcardDirectories map[string]bool
|
||||
includeGlobsOnce sync.Once
|
||||
includeGlobs []*glob.Glob
|
||||
extraFileExtensions []FileExtensionInfo
|
||||
|
||||
sourceAndOutputMapsOnce sync.Once
|
||||
sourceToProjectReference map[tspath.Path]*SourceOutputAndProjectReference
|
||||
outputDtsToProjectReference map[tspath.Path]*SourceOutputAndProjectReference
|
||||
|
||||
commonSourceDirectory string
|
||||
commonSourceDirectoryOnce sync.Once
|
||||
|
||||
resolvedProjectReferencePaths []string
|
||||
resolvedProjectReferencePathsOnce sync.Once
|
||||
|
||||
literalFileNamesLen int
|
||||
fileNamesByPath map[tspath.Path]string // maps file names to their paths, used for quick lookups
|
||||
fileNamesByPathOnce sync.Once
|
||||
|
||||
locale locale.Locale
|
||||
localeOnce sync.Once
|
||||
}
|
||||
|
||||
func NewParsedCommandLine(
|
||||
compilerOptions *core.CompilerOptions,
|
||||
rootFileNames []string,
|
||||
comparePathsOptions tspath.ComparePathsOptions,
|
||||
) *ParsedCommandLine {
|
||||
return &ParsedCommandLine{
|
||||
ParsedConfig: &core.ParsedOptions{
|
||||
CompilerOptions: compilerOptions,
|
||||
FileNames: rootFileNames,
|
||||
},
|
||||
comparePathsOptions: comparePathsOptions,
|
||||
}
|
||||
}
|
||||
|
||||
type SourceOutputAndProjectReference struct {
|
||||
Source string
|
||||
OutputDts string
|
||||
Resolved *ParsedCommandLine
|
||||
}
|
||||
|
||||
var (
|
||||
_ module.ResolvedProjectReference = (*ParsedCommandLine)(nil)
|
||||
_ outputpaths.OutputPathsHost = (*ParsedCommandLine)(nil)
|
||||
)
|
||||
|
||||
func (p *ParsedCommandLine) ConfigName() string {
|
||||
if p == nil || p.ConfigFile == nil {
|
||||
return ""
|
||||
}
|
||||
return p.ConfigFile.SourceFile.FileName()
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) SourceToProjectReference() map[tspath.Path]*SourceOutputAndProjectReference {
|
||||
return p.sourceToProjectReference
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) OutputDtsToProjectReference() map[tspath.Path]*SourceOutputAndProjectReference {
|
||||
return p.outputDtsToProjectReference
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) ParseInputOutputNames() {
|
||||
p.sourceAndOutputMapsOnce.Do(func() {
|
||||
sourceToOutput := map[tspath.Path]*SourceOutputAndProjectReference{}
|
||||
outputDtsToSource := map[tspath.Path]*SourceOutputAndProjectReference{}
|
||||
|
||||
for outputDts, source := range p.getOutputDeclarationAndSourceFileNames() {
|
||||
path := tspath.ToPath(source, p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames())
|
||||
projectReference := &SourceOutputAndProjectReference{
|
||||
Source: source,
|
||||
OutputDts: outputDts,
|
||||
Resolved: p,
|
||||
}
|
||||
if outputDts != "" {
|
||||
outputDtsToSource[tspath.ToPath(outputDts, p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames())] = projectReference
|
||||
}
|
||||
sourceToOutput[path] = projectReference
|
||||
}
|
||||
p.outputDtsToProjectReference = outputDtsToSource
|
||||
p.sourceToProjectReference = sourceToOutput
|
||||
})
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) CommonSourceDirectory() string {
|
||||
p.commonSourceDirectoryOnce.Do(func() {
|
||||
files := func() []string {
|
||||
return core.Filter(p.ParsedConfig.FileNames, func(file string) bool {
|
||||
return !(p.ParsedConfig.CompilerOptions.NoEmitForJsFiles.IsTrue() && tspath.HasJSFileExtension(file)) && !tspath.IsDeclarationFileName(file)
|
||||
})
|
||||
}
|
||||
|
||||
p.commonSourceDirectory = outputpaths.GetCommonSourceDirectory(
|
||||
p.ParsedConfig.CompilerOptions,
|
||||
files,
|
||||
p.GetCurrentDirectory(),
|
||||
p.UseCaseSensitiveFileNames(),
|
||||
p.checkSourceFilesBelongToPath,
|
||||
)
|
||||
})
|
||||
return p.commonSourceDirectory
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) checkSourceFilesBelongToPath(sourceFiles []string, rootDirectory string) bool {
|
||||
allFilesBelongToPath := true
|
||||
for _, file := range sourceFiles {
|
||||
absoluteSourceFilePath := tspath.GetCanonicalFileName(tspath.GetNormalizedAbsolutePath(file, p.GetCurrentDirectory()), p.UseCaseSensitiveFileNames())
|
||||
if !tspath.ContainsPath(rootDirectory, file, p.comparePathsOptions) {
|
||||
p.Errors = append(p.Errors, ast.NewCompilerDiagnostic(diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, absoluteSourceFilePath, rootDirectory))
|
||||
allFilesBelongToPath = false
|
||||
}
|
||||
}
|
||||
|
||||
return allFilesBelongToPath
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) GetCurrentDirectory() string {
|
||||
return p.comparePathsOptions.CurrentDirectory
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) UseCaseSensitiveFileNames() bool {
|
||||
return p.comparePathsOptions.UseCaseSensitiveFileNames
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) getOutputDeclarationAndSourceFileNames() iter.Seq2[string, string] {
|
||||
return func(yield func(dtsName string, inputName string) bool) {
|
||||
for _, fileName := range p.ParsedConfig.FileNames {
|
||||
var outputDts string
|
||||
if !tspath.IsDeclarationFileName(fileName) && !tspath.FileExtensionIs(fileName, tspath.ExtensionJson) {
|
||||
outputDts = outputpaths.GetOutputDeclarationFileNameWorker(fileName, p.CompilerOptions(), p)
|
||||
}
|
||||
if !yield(outputDts, fileName) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) GetOutputFileNames() iter.Seq[string] {
|
||||
return func(yield func(outputName string) bool) {
|
||||
for _, fileName := range p.ParsedConfig.FileNames {
|
||||
if tspath.IsDeclarationFileName(fileName) {
|
||||
continue
|
||||
}
|
||||
jsFileName := outputpaths.GetOutputJSFileName(fileName, p.CompilerOptions(), p)
|
||||
isJson := tspath.FileExtensionIs(fileName, tspath.ExtensionJson)
|
||||
if jsFileName != "" {
|
||||
if !yield(jsFileName) {
|
||||
return
|
||||
}
|
||||
if !isJson {
|
||||
sourceMap := outputpaths.GetSourceMapFilePath(jsFileName, p.CompilerOptions())
|
||||
if sourceMap != "" {
|
||||
if !yield(sourceMap) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if isJson {
|
||||
continue
|
||||
}
|
||||
if p.CompilerOptions().GetEmitDeclarations() {
|
||||
dtsFileName := outputpaths.GetOutputDeclarationFileNameWorker(fileName, p.CompilerOptions(), p)
|
||||
if dtsFileName != "" {
|
||||
if !yield(dtsFileName) {
|
||||
return
|
||||
}
|
||||
if p.CompilerOptions().GetAreDeclarationMapsEnabled() {
|
||||
declarationMap := dtsFileName + ".map"
|
||||
if !yield(declarationMap) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) GetBuildInfoFileName() string {
|
||||
return outputpaths.GetBuildInfoFileName(p.CompilerOptions(), p.comparePathsOptions)
|
||||
}
|
||||
|
||||
// WildcardDirectories returns the cached wildcard directories, initializing them if needed
|
||||
func (p *ParsedCommandLine) WildcardDirectories() map[string]bool {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.wildcardDirectoriesOnce.Do(func() {
|
||||
if p.wildcardDirectories == nil {
|
||||
p.wildcardDirectories = getWildcardDirectories(
|
||||
p.ConfigFile.configFileSpecs.validatedIncludeSpecs,
|
||||
p.ConfigFile.configFileSpecs.validatedExcludeSpecs,
|
||||
p.comparePathsOptions,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return p.wildcardDirectories
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) WildcardDirectoryGlobs() []*glob.Glob {
|
||||
wildcardDirectories := p.WildcardDirectories()
|
||||
if wildcardDirectories == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.includeGlobsOnce.Do(func() {
|
||||
if p.includeGlobs == nil {
|
||||
globs := make([]*glob.Glob, 0, len(wildcardDirectories))
|
||||
for dir, recursive := range wildcardDirectories {
|
||||
if parsed, err := glob.Parse(fmt.Sprintf("%s/%s", tspath.NormalizePath(dir), core.IfElse(recursive, recursiveFileGlobPattern, fileGlobPattern))); err == nil {
|
||||
globs = append(globs, parsed)
|
||||
}
|
||||
}
|
||||
p.includeGlobs = globs
|
||||
}
|
||||
})
|
||||
|
||||
return p.includeGlobs
|
||||
}
|
||||
|
||||
// Normalized file names explicitly specified in `files`
|
||||
func (p *ParsedCommandLine) LiteralFileNames() []string {
|
||||
if p != nil && p.ConfigFile != nil {
|
||||
return p.FileNames()[0:p.literalFileNamesLen]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) SetParsedOptions(o *core.ParsedOptions) {
|
||||
p.ParsedConfig = o
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) SetCompilerOptions(o *core.CompilerOptions) {
|
||||
p.ParsedConfig.CompilerOptions = o
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) CompilerOptions() *core.CompilerOptions {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return p.ParsedConfig.CompilerOptions
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) SetTypeAcquisition(o *core.TypeAcquisition) {
|
||||
p.ParsedConfig.TypeAcquisition = o
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) TypeAcquisition() *core.TypeAcquisition {
|
||||
return p.ParsedConfig.TypeAcquisition
|
||||
}
|
||||
|
||||
// All file names matched by files, include, and exclude patterns
|
||||
func (p *ParsedCommandLine) FileNames() []string {
|
||||
return p.ParsedConfig.FileNames
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) FileNamesByPath() map[tspath.Path]string {
|
||||
p.fileNamesByPathOnce.Do(func() {
|
||||
p.fileNamesByPath = make(map[tspath.Path]string, len(p.ParsedConfig.FileNames))
|
||||
for _, fileName := range p.ParsedConfig.FileNames {
|
||||
path := tspath.ToPath(fileName, p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames())
|
||||
p.fileNamesByPath[path] = fileName
|
||||
}
|
||||
})
|
||||
return p.fileNamesByPath
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) ProjectReferences() []*core.ProjectReference {
|
||||
return p.ParsedConfig.ProjectReferences
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) ResolvedProjectReferencePaths() []string {
|
||||
p.resolvedProjectReferencePathsOnce.Do(func() {
|
||||
p.resolvedProjectReferencePaths = core.Map(p.ParsedConfig.ProjectReferences, core.ResolveProjectReferencePath)
|
||||
})
|
||||
return p.resolvedProjectReferencePaths
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) ExtendedSourceFiles() []string {
|
||||
if p == nil || p.ConfigFile == nil {
|
||||
return nil
|
||||
}
|
||||
return p.ConfigFile.ExtendedSourceFiles
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) GetConfigFileParsingDiagnostics() []*ast.Diagnostic {
|
||||
if p.ConfigFile != nil {
|
||||
// todo: !!! should be ConfigFile.ParseDiagnostics, check if they are the same
|
||||
return slices.Concat(p.ConfigFile.SourceFile.Diagnostics(), p.Errors)
|
||||
}
|
||||
return p.Errors
|
||||
}
|
||||
|
||||
// PossiblyMatchesFileName is a fast check to see if a file is currently included by a config
|
||||
// or would be included if the file were to be created. It may return false positives.
|
||||
func (p *ParsedCommandLine) PossiblyMatchesFileName(fileName string) bool {
|
||||
path := tspath.ToPath(fileName, p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames())
|
||||
if _, ok := p.FileNamesByPath()[path]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, include := range p.ConfigFile.configFileSpecs.validatedIncludeSpecs {
|
||||
if !strings.ContainsAny(include, "*?") && !vfsmatch.IsImplicitGlob(include) {
|
||||
includePath := tspath.ToPath(include, p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames())
|
||||
if includePath == path {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if wildcardDirectoryGlobs := p.WildcardDirectoryGlobs(); len(wildcardDirectoryGlobs) > 0 {
|
||||
for _, glob := range wildcardDirectoryGlobs {
|
||||
if glob.Match(fileName) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) PossiblyMatchesDirectoryName(directoryPath tspath.Path) bool {
|
||||
for wildcardDir, recursive := range p.WildcardDirectories() {
|
||||
wildcardDirPath := tspath.ToPath(wildcardDir, p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames())
|
||||
if recursive {
|
||||
if wildcardDirPath.ContainsPath(directoryPath) {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if wildcardDirPath == directoryPath {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) GetMatchedFileSpec(fileName string) string {
|
||||
return p.ConfigFile.configFileSpecs.getMatchedFileSpec(fileName, p.comparePathsOptions)
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) GetMatchedIncludeSpec(fileName string) (string, bool) {
|
||||
if len(p.ConfigFile.configFileSpecs.validatedIncludeSpecs) == 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if p.ConfigFile.configFileSpecs.isDefaultIncludeSpec {
|
||||
return p.ConfigFile.configFileSpecs.validatedIncludeSpecs[0], true
|
||||
}
|
||||
|
||||
return p.ConfigFile.configFileSpecs.getMatchedIncludeSpec(fileName, p.comparePathsOptions), false
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) ReloadFileNamesOfParsedCommandLine(fs vfs.FS) *ParsedCommandLine {
|
||||
parsedConfig := *p.ParsedConfig
|
||||
fileNames, literalFileNamesLen := getFileNamesFromConfigSpecs(
|
||||
*p.ConfigFile.configFileSpecs,
|
||||
p.GetCurrentDirectory(),
|
||||
p.CompilerOptions(),
|
||||
fs,
|
||||
p.extraFileExtensions,
|
||||
)
|
||||
parsedConfig.FileNames = fileNames
|
||||
parsedCommandLine := ParsedCommandLine{
|
||||
ParsedConfig: &parsedConfig,
|
||||
ConfigFile: p.ConfigFile,
|
||||
Errors: p.Errors,
|
||||
Raw: p.Raw,
|
||||
CompileOnSave: p.CompileOnSave,
|
||||
comparePathsOptions: p.comparePathsOptions,
|
||||
wildcardDirectories: p.wildcardDirectories,
|
||||
includeGlobs: p.includeGlobs,
|
||||
extraFileExtensions: p.extraFileExtensions,
|
||||
literalFileNamesLen: literalFileNamesLen,
|
||||
}
|
||||
return &parsedCommandLine
|
||||
}
|
||||
|
||||
func (p *ParsedCommandLine) Locale() locale.Locale {
|
||||
p.localeOnce.Do(func() {
|
||||
p.locale, _ = locale.Parse(p.CompilerOptions().Locale)
|
||||
})
|
||||
return p.locale
|
||||
}
|
||||
Reference in New Issue
Block a user