vendor tsgo
This commit is contained in:
250
tools/tsgo/internal/modulespecifiers/preferences.go
Normal file
250
tools/tsgo/internal/modulespecifiers/preferences.go
Normal file
@@ -0,0 +1,250 @@
|
||||
package modulespecifiers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/debug"
|
||||
"github.com/microsoft/typescript-go/internal/tspath"
|
||||
)
|
||||
|
||||
// Program errors validate that `noEmit` or `emitDeclarationOnly` is also set,
|
||||
// so this function doesn't check them to avoid propagating errors.
|
||||
func shouldAllowImportingTsExtension(compilerOptions *core.CompilerOptions, fromFileName string) bool {
|
||||
return compilerOptions.GetAllowImportingTsExtensions() || len(fromFileName) > 0 && tspath.IsDeclarationFileName(fromFileName)
|
||||
}
|
||||
|
||||
func usesExtensionsOnImports(file SourceFileForSpecifierGeneration) bool {
|
||||
for _, ref := range file.Imports() {
|
||||
text := ref.Text()
|
||||
if tspath.PathIsRelative(text) && !tspath.FileExtensionIsOneOf(text, tspath.ExtensionsNotSupportingExtensionlessResolution) {
|
||||
return tspath.HasTSFileExtension(text) || tspath.HasJSFileExtension(text)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func inferPreference(
|
||||
resolutionMode core.ResolutionMode,
|
||||
sourceFile SourceFileForSpecifierGeneration,
|
||||
moduleResolutionIsNodeNext bool,
|
||||
) ModuleSpecifierEnding {
|
||||
usesJsExtensions := false
|
||||
var specifiers []*ast.LiteralLikeNode
|
||||
if sourceFile != nil && len(sourceFile.Imports()) > 0 {
|
||||
specifiers = sourceFile.Imports()
|
||||
} else if sourceFile != nil && sourceFile.IsJS() {
|
||||
// !!! TODO: JS support
|
||||
// specifiers = core.Map(getRequiresAtTopOfFile(sourceFile), func(d *ast.Node) *ast.Node { return d.arguments[0] })
|
||||
}
|
||||
|
||||
for _, specifier := range specifiers {
|
||||
path := specifier.Text()
|
||||
if tspath.PathIsRelative(path) {
|
||||
// !!! TODO: proper resolutionMode support
|
||||
if moduleResolutionIsNodeNext && resolutionMode == core.ResolutionModeCommonJS /* && getModeForUsageLocation(sourceFile!, specifier, compilerOptions) === ModuleKind.ESNext */ {
|
||||
// We're trying to decide a preference for a CommonJS module specifier, but looking at an ESM import.
|
||||
continue
|
||||
}
|
||||
if tspath.FileExtensionIsOneOf(path, tspath.ExtensionsNotSupportingExtensionlessResolution) {
|
||||
// These extensions are not optional, so do not indicate a preference.
|
||||
continue
|
||||
}
|
||||
if tspath.HasTSFileExtension(path) {
|
||||
return ModuleSpecifierEndingTsExtension
|
||||
}
|
||||
if tspath.HasJSFileExtension(path) {
|
||||
usesJsExtensions = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if usesJsExtensions {
|
||||
return ModuleSpecifierEndingJsExtension
|
||||
}
|
||||
return ModuleSpecifierEndingMinimal
|
||||
}
|
||||
|
||||
func getModuleSpecifierEndingPreference(
|
||||
pref ImportModuleSpecifierEndingPreference,
|
||||
resolutionMode core.ResolutionMode,
|
||||
compilerOptions *core.CompilerOptions,
|
||||
sourceFile SourceFileForSpecifierGeneration,
|
||||
) ModuleSpecifierEnding {
|
||||
moduleResolution := compilerOptions.GetModuleResolutionKind()
|
||||
moduleResolutionIsNodeNext := core.ModuleResolutionKindNode16 <= moduleResolution && moduleResolution <= core.ModuleResolutionKindNodeNext
|
||||
|
||||
if pref == ImportModuleSpecifierEndingPreferenceJs || resolutionMode == core.ResolutionModeESM && moduleResolutionIsNodeNext {
|
||||
// Extensions are explicitly requested or required. Now choose between .js and .ts.
|
||||
if !shouldAllowImportingTsExtension(compilerOptions, "") {
|
||||
return ModuleSpecifierEndingJsExtension
|
||||
}
|
||||
// `allowImportingTsExtensions` is a strong signal, so use .ts unless the file
|
||||
// already uses .js extensions and no .ts extensions.
|
||||
if inferPreference(resolutionMode, sourceFile, moduleResolutionIsNodeNext) != ModuleSpecifierEndingJsExtension {
|
||||
return ModuleSpecifierEndingTsExtension
|
||||
}
|
||||
return ModuleSpecifierEndingJsExtension
|
||||
}
|
||||
|
||||
if pref == ImportModuleSpecifierEndingPreferenceMinimal {
|
||||
return ModuleSpecifierEndingMinimal
|
||||
}
|
||||
|
||||
if pref == ImportModuleSpecifierEndingPreferenceIndex {
|
||||
return ModuleSpecifierEndingIndex
|
||||
}
|
||||
|
||||
// No preference was specified.
|
||||
// Look at imports and/or requires to guess whether .js, .ts, or extensionless imports are preferred.
|
||||
// N.B. that `Index` detection is not supported since it would require file system probing to do
|
||||
// accurately, and more importantly, literally nobody wants `Index` and its existence is a mystery.
|
||||
if !shouldAllowImportingTsExtension(compilerOptions, "") {
|
||||
// If .ts imports are not valid, we only need to see one .js import to go with that.
|
||||
if sourceFile != nil && usesExtensionsOnImports(sourceFile) {
|
||||
return ModuleSpecifierEndingJsExtension
|
||||
}
|
||||
return ModuleSpecifierEndingMinimal
|
||||
}
|
||||
|
||||
return inferPreference(resolutionMode, sourceFile, moduleResolutionIsNodeNext)
|
||||
}
|
||||
|
||||
func getPreferredEnding(
|
||||
prefs UserPreferences,
|
||||
host ModuleSpecifierGenerationHost,
|
||||
compilerOptions *core.CompilerOptions,
|
||||
importingSourceFile SourceFileForSpecifierGeneration,
|
||||
oldImportSpecifier string,
|
||||
resolutionMode core.ResolutionMode,
|
||||
) ModuleSpecifierEnding {
|
||||
if len(oldImportSpecifier) > 0 {
|
||||
if tspath.HasJSFileExtension(oldImportSpecifier) {
|
||||
return ModuleSpecifierEndingJsExtension
|
||||
}
|
||||
if strings.HasSuffix(oldImportSpecifier, "/index") {
|
||||
return ModuleSpecifierEndingIndex
|
||||
}
|
||||
}
|
||||
if resolutionMode == core.ResolutionModeNone {
|
||||
resolutionMode = host.GetDefaultResolutionModeForFile(importingSourceFile)
|
||||
}
|
||||
return getModuleSpecifierEndingPreference(
|
||||
prefs.ImportModuleSpecifierEnding,
|
||||
resolutionMode,
|
||||
compilerOptions,
|
||||
importingSourceFile,
|
||||
)
|
||||
}
|
||||
|
||||
type ModuleSpecifierPreferences struct {
|
||||
relativePreference RelativePreferenceKind
|
||||
getAllowedEndingsInPreferredOrder func(syntaxImpliedNodeFormat core.ResolutionMode) []ModuleSpecifierEnding
|
||||
excludeRegexes []string
|
||||
}
|
||||
|
||||
func GetAllowedEndingsInPreferredOrder(
|
||||
prefs UserPreferences,
|
||||
host ModuleSpecifierGenerationHost,
|
||||
compilerOptions *core.CompilerOptions,
|
||||
importingSourceFile SourceFileForSpecifierGeneration,
|
||||
oldImportSpecifier string,
|
||||
syntaxImpliedNodeFormat core.ResolutionMode,
|
||||
) []ModuleSpecifierEnding {
|
||||
preferredEnding := getPreferredEnding(
|
||||
prefs,
|
||||
host,
|
||||
compilerOptions,
|
||||
importingSourceFile,
|
||||
oldImportSpecifier,
|
||||
core.ResolutionModeNone,
|
||||
)
|
||||
resolutionMode := host.GetDefaultResolutionModeForFile(importingSourceFile)
|
||||
if resolutionMode != syntaxImpliedNodeFormat {
|
||||
preferredEnding = getPreferredEnding(
|
||||
prefs,
|
||||
host,
|
||||
compilerOptions,
|
||||
importingSourceFile,
|
||||
oldImportSpecifier,
|
||||
syntaxImpliedNodeFormat,
|
||||
)
|
||||
}
|
||||
moduleResolution := compilerOptions.GetModuleResolutionKind()
|
||||
moduleResolutionIsNodeNext := core.ModuleResolutionKindNode16 <= moduleResolution && moduleResolution <= core.ModuleResolutionKindNodeNext
|
||||
allowImportingTsExtension := shouldAllowImportingTsExtension(compilerOptions, importingSourceFile.FileName())
|
||||
if syntaxImpliedNodeFormat == core.ResolutionModeESM && moduleResolutionIsNodeNext {
|
||||
if allowImportingTsExtension {
|
||||
return []ModuleSpecifierEnding{ModuleSpecifierEndingTsExtension, ModuleSpecifierEndingJsExtension}
|
||||
}
|
||||
return []ModuleSpecifierEnding{ModuleSpecifierEndingJsExtension}
|
||||
}
|
||||
switch preferredEnding {
|
||||
case ModuleSpecifierEndingJsExtension:
|
||||
if allowImportingTsExtension {
|
||||
return []ModuleSpecifierEnding{ModuleSpecifierEndingJsExtension, ModuleSpecifierEndingTsExtension, ModuleSpecifierEndingMinimal, ModuleSpecifierEndingIndex}
|
||||
}
|
||||
return []ModuleSpecifierEnding{ModuleSpecifierEndingJsExtension, ModuleSpecifierEndingMinimal, ModuleSpecifierEndingIndex}
|
||||
case ModuleSpecifierEndingTsExtension:
|
||||
return []ModuleSpecifierEnding{ModuleSpecifierEndingTsExtension, ModuleSpecifierEndingMinimal, ModuleSpecifierEndingJsExtension, ModuleSpecifierEndingIndex}
|
||||
case ModuleSpecifierEndingIndex:
|
||||
if allowImportingTsExtension {
|
||||
return []ModuleSpecifierEnding{ModuleSpecifierEndingIndex, ModuleSpecifierEndingMinimal, ModuleSpecifierEndingTsExtension, ModuleSpecifierEndingJsExtension}
|
||||
}
|
||||
return []ModuleSpecifierEnding{ModuleSpecifierEndingIndex, ModuleSpecifierEndingMinimal, ModuleSpecifierEndingJsExtension}
|
||||
case ModuleSpecifierEndingMinimal:
|
||||
if allowImportingTsExtension {
|
||||
return []ModuleSpecifierEnding{ModuleSpecifierEndingMinimal, ModuleSpecifierEndingIndex, ModuleSpecifierEndingTsExtension, ModuleSpecifierEndingJsExtension}
|
||||
}
|
||||
return []ModuleSpecifierEnding{ModuleSpecifierEndingMinimal, ModuleSpecifierEndingIndex, ModuleSpecifierEndingJsExtension}
|
||||
default:
|
||||
debug.AssertNever(preferredEnding)
|
||||
}
|
||||
return []ModuleSpecifierEnding{ModuleSpecifierEndingMinimal}
|
||||
}
|
||||
|
||||
func getModuleSpecifierPreferences(
|
||||
prefs UserPreferences,
|
||||
host ModuleSpecifierGenerationHost,
|
||||
compilerOptions *core.CompilerOptions,
|
||||
importingSourceFile SourceFileForSpecifierGeneration,
|
||||
oldImportSpecifier string,
|
||||
) ModuleSpecifierPreferences {
|
||||
excludes := prefs.AutoImportSpecifierExcludeRegexes
|
||||
relativePreference := RelativePreferenceShortest
|
||||
if len(oldImportSpecifier) > 0 {
|
||||
if tspath.IsExternalModuleNameRelative(oldImportSpecifier) {
|
||||
relativePreference = RelativePreferenceRelative
|
||||
} else {
|
||||
relativePreference = RelativePreferenceNonRelative
|
||||
}
|
||||
} else {
|
||||
switch prefs.ImportModuleSpecifierPreference {
|
||||
case ImportModuleSpecifierPreferenceRelative:
|
||||
relativePreference = RelativePreferenceRelative
|
||||
case ImportModuleSpecifierPreferenceNonRelative:
|
||||
relativePreference = RelativePreferenceNonRelative
|
||||
case ImportModuleSpecifierPreferenceProjectRelative:
|
||||
relativePreference = RelativePreferenceExternalNonRelative
|
||||
// all others are shortest
|
||||
}
|
||||
}
|
||||
|
||||
getAllowedEndingsInPreferredOrder := func(syntaxImpliedNodeFormat core.ResolutionMode) []ModuleSpecifierEnding {
|
||||
return GetAllowedEndingsInPreferredOrder(
|
||||
prefs,
|
||||
host,
|
||||
compilerOptions,
|
||||
importingSourceFile,
|
||||
oldImportSpecifier,
|
||||
syntaxImpliedNodeFormat,
|
||||
)
|
||||
}
|
||||
|
||||
return ModuleSpecifierPreferences{
|
||||
excludeRegexes: excludes,
|
||||
relativePreference: relativePreference,
|
||||
getAllowedEndingsInPreferredOrder: getAllowedEndingsInPreferredOrder,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user