129 lines
4.7 KiB
Go
129 lines
4.7 KiB
Go
package ls
|
|
|
|
import (
|
|
"cmp"
|
|
"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/modulespecifiers"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
|
)
|
|
|
|
// statement = anyImportOrRequireStatement
|
|
func getImportDeclarationInsertIndex(sortedImports []*ast.Statement, newImport *ast.Statement, comparer func(a, b *ast.Statement) int) int {
|
|
// !!!
|
|
return len(sortedImports)
|
|
}
|
|
|
|
// returns `-1` if `a` is better than `b`
|
|
//
|
|
// note: this sorts in descending order of preference; different than convention in other cmp-like functions
|
|
func compareModuleSpecifiers(
|
|
a *ImportFix, // !!! ImportFixWithModuleSpecifier
|
|
b *ImportFix, // !!! ImportFixWithModuleSpecifier
|
|
importingFile *ast.SourceFile, // | FutureSourceFile,
|
|
program *compiler.Program,
|
|
preferences UserPreferences,
|
|
allowsImportingSpecifier func(specifier string) bool,
|
|
toPath func(fileName string) tspath.Path,
|
|
) int {
|
|
if a.kind == ImportFixKindUseNamespace || b.kind == ImportFixKindUseNamespace {
|
|
return 0
|
|
}
|
|
if comparison := compareBooleans(
|
|
b.moduleSpecifierKind != modulespecifiers.ResultKindNodeModules || allowsImportingSpecifier(b.moduleSpecifier),
|
|
a.moduleSpecifierKind != modulespecifiers.ResultKindNodeModules || allowsImportingSpecifier(a.moduleSpecifier),
|
|
); comparison != 0 {
|
|
return comparison
|
|
}
|
|
if comparison := compareModuleSpecifierRelativity(a, b, preferences); comparison != 0 {
|
|
return comparison
|
|
}
|
|
if comparison := compareNodeCoreModuleSpecifiers(a.moduleSpecifier, b.moduleSpecifier, importingFile, program); comparison != 0 {
|
|
return comparison
|
|
}
|
|
if comparison := compareBooleans(isFixPossiblyReExportingImportingFile(a, importingFile.Path(), toPath), isFixPossiblyReExportingImportingFile(b, importingFile.Path(), toPath)); comparison != 0 {
|
|
return comparison
|
|
}
|
|
if comparison := compareNumberOfDirectorySeparators(a.moduleSpecifier, b.moduleSpecifier); comparison != 0 {
|
|
return comparison
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// True > False
|
|
func compareBooleans(a, b bool) int {
|
|
if a && !b {
|
|
return -1
|
|
} else if !a && b {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// returns `-1` if `a` is better than `b`
|
|
func compareModuleSpecifierRelativity(a *ImportFix, b *ImportFix, preferences UserPreferences) int {
|
|
switch preferences.ImportModuleSpecifierPreference {
|
|
case modulespecifiers.ImportModuleSpecifierPreferenceNonRelative, modulespecifiers.ImportModuleSpecifierPreferenceProjectRelative:
|
|
return compareBooleans(a.moduleSpecifierKind == modulespecifiers.ResultKindRelative, b.moduleSpecifierKind == modulespecifiers.ResultKindRelative)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func compareNodeCoreModuleSpecifiers(a, b string, importingFile *ast.SourceFile, program *compiler.Program) int {
|
|
if strings.HasPrefix(a, "node:") && !strings.HasPrefix(b, "node:") {
|
|
if shouldUseUriStyleNodeCoreModules(importingFile, program) {
|
|
return -1
|
|
}
|
|
return 1
|
|
}
|
|
if strings.HasPrefix(b, "node:") && !strings.HasPrefix(a, "node:") {
|
|
if shouldUseUriStyleNodeCoreModules(importingFile, program) {
|
|
return 1
|
|
}
|
|
return -1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func shouldUseUriStyleNodeCoreModules(file *ast.SourceFile, program *compiler.Program) bool {
|
|
for _, node := range file.Imports() {
|
|
if core.NodeCoreModules()[node.Text()] && !core.ExclusivelyPrefixedNodeCoreModules[node.Text()] {
|
|
if strings.HasPrefix(node.Text(), "node:") {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
return program.UsesUriStyleNodeCoreModules()
|
|
}
|
|
|
|
// This is a simple heuristic to try to avoid creating an import cycle with a barrel re-export.
|
|
// E.g., do not `import { Foo } from ".."` when you could `import { Foo } from "../Foo"`.
|
|
// This can produce false positives or negatives if re-exports cross into sibling directories
|
|
// (e.g. `export * from "../whatever"`) or are not named "index".
|
|
func isFixPossiblyReExportingImportingFile(fix *ImportFix, importingFilePath tspath.Path, toPath func(fileName string) tspath.Path) bool {
|
|
if fix.isReExport != nil && *(fix.isReExport) &&
|
|
fix.exportInfo != nil && fix.exportInfo.moduleFileName != "" && isIndexFileName(fix.exportInfo.moduleFileName) {
|
|
reExportDir := toPath(tspath.GetDirectoryPath(fix.exportInfo.moduleFileName))
|
|
return strings.HasPrefix(string(importingFilePath), string(reExportDir))
|
|
}
|
|
return false
|
|
}
|
|
|
|
func compareNumberOfDirectorySeparators(path1, path2 string) int {
|
|
return cmp.Compare(strings.Count(path1, "/"), strings.Count(path2, "/"))
|
|
}
|
|
|
|
func isIndexFileName(fileName string) bool {
|
|
fileName = tspath.GetBaseFileName(fileName)
|
|
if tspath.FileExtensionIsOneOf(fileName, []string{".js", ".jsx", ".d.ts", ".ts", ".tsx"}) {
|
|
fileName = tspath.RemoveFileExtension(fileName)
|
|
}
|
|
return fileName == "index"
|
|
}
|