2025-11-08 09:37:30 +03:00

179 lines
6.4 KiB
Go

package tspath
import (
"path/filepath"
"slices"
"strings"
)
const (
ExtensionTs = ".ts"
ExtensionTsx = ".tsx"
ExtensionDts = ".d.ts"
ExtensionJs = ".js"
ExtensionJsx = ".jsx"
ExtensionJson = ".json"
ExtensionTsBuildInfo = ".tsbuildinfo"
ExtensionMjs = ".mjs"
ExtensionMts = ".mts"
ExtensionDmts = ".d.mts"
ExtensionCjs = ".cjs"
ExtensionCts = ".cts"
ExtensionDcts = ".d.cts"
)
var (
supportedDeclarationExtensions = []string{ExtensionDts, ExtensionDcts, ExtensionDmts}
supportedTSImplementationExtensions = []string{ExtensionTs, ExtensionTsx, ExtensionMts, ExtensionCts}
supportedTSExtensionsForExtractExtension = []string{ExtensionDts, ExtensionDcts, ExtensionDmts, ExtensionTs, ExtensionTsx, ExtensionMts, ExtensionCts}
AllSupportedExtensions = [][]string{{ExtensionTs, ExtensionTsx, ExtensionDts, ExtensionJs, ExtensionJsx}, {ExtensionCts, ExtensionDcts, ExtensionCjs}, {ExtensionMts, ExtensionDmts, ExtensionMjs}}
SupportedTSExtensions = [][]string{{ExtensionTs, ExtensionTsx, ExtensionDts}, {ExtensionCts, ExtensionDcts}, {ExtensionMts, ExtensionDmts}}
SupportedTSExtensionsFlat = []string{ExtensionTs, ExtensionTsx, ExtensionDts, ExtensionCts, ExtensionDcts, ExtensionMts, ExtensionDmts}
SupportedJSExtensions = [][]string{{ExtensionJs, ExtensionJsx}, {ExtensionMjs}, {ExtensionCjs}}
SupportedJSExtensionsFlat = []string{ExtensionJs, ExtensionJsx, ExtensionMjs, ExtensionCjs}
AllSupportedExtensionsWithJson = slices.Concat(AllSupportedExtensions, [][]string{{ExtensionJson}})
SupportedTSExtensionsWithJson = slices.Concat(SupportedTSExtensions, [][]string{{ExtensionJson}})
SupportedTSExtensionsWithJsonFlat = slices.Concat(SupportedTSExtensionsFlat, []string{ExtensionJson})
ExtensionsNotSupportingExtensionlessResolution = []string{ExtensionMts, ExtensionDmts, ExtensionMjs, ExtensionCts, ExtensionDcts, ExtensionCjs}
)
func ExtensionIsTs(ext string) bool {
return ext == ExtensionTs || ext == ExtensionTsx || ext == ExtensionDts || ext == ExtensionMts || ext == ExtensionDmts || ext == ExtensionCts || ext == ExtensionDcts || len(ext) >= 7 && ext[:3] == ".d." && ext[len(ext)-3:] == ".ts"
}
var extensionsToRemove = []string{ExtensionDts, ExtensionDmts, ExtensionDcts, ExtensionMjs, ExtensionMts, ExtensionCjs, ExtensionCts, ExtensionTs, ExtensionJs, ExtensionTsx, ExtensionJsx, ExtensionJson}
func RemoveFileExtension(path string) string {
// Remove any known extension even if it has more than one dot
for _, ext := range extensionsToRemove {
if strings.HasSuffix(path, ext) {
return path[:len(path)-len(ext)]
}
}
// Otherwise just remove single dot extension, if any
return path[:len(path)-len(filepath.Ext(path))] //nolint:forbidigo
}
func TryGetExtensionFromPath(p string) string {
for _, ext := range extensionsToRemove {
if FileExtensionIs(p, ext) {
return ext
}
}
return ""
}
func RemoveExtension(path string, extension string) string {
return path[:len(path)-len(extension)]
}
func FileExtensionIsOneOf(path string, extensions []string) bool {
for _, ext := range extensions {
if FileExtensionIs(path, ext) {
return true
}
}
return false
}
func TryExtractTSExtension(fileName string) string {
for _, ext := range supportedTSExtensionsForExtractExtension {
if FileExtensionIs(fileName, ext) {
return ext
}
}
return ""
}
func HasTSFileExtension(path string) bool {
return FileExtensionIsOneOf(path, SupportedTSExtensionsFlat)
}
func HasImplementationTSFileExtension(path string) bool {
return FileExtensionIsOneOf(path, supportedTSImplementationExtensions) && !IsDeclarationFileName(path)
}
func HasJSFileExtension(path string) bool {
return FileExtensionIsOneOf(path, SupportedJSExtensionsFlat)
}
func HasJSONFileExtension(path string) bool {
return FileExtensionIs(path, ExtensionJson)
}
func IsDeclarationFileName(fileName string) bool {
return GetDeclarationFileExtension(fileName) != ""
}
func ExtensionIsOneOf(ext string, extensions []string) bool {
return slices.Contains(extensions, ext)
}
func GetDeclarationFileExtension(fileName string) string {
base := GetBaseFileName(fileName)
for _, ext := range supportedDeclarationExtensions {
if strings.HasSuffix(base, ext) {
return ext
}
}
if strings.HasSuffix(base, ExtensionTs) {
index := strings.Index(base, ".d.")
if index >= 0 {
return base[index:]
}
}
return ""
}
func GetDeclarationEmitExtensionForPath(path string) string {
switch {
case FileExtensionIsOneOf(path, []string{ExtensionMjs, ExtensionMts}):
return ExtensionDmts
case FileExtensionIsOneOf(path, []string{ExtensionCjs, ExtensionCts}):
return ExtensionDcts
case FileExtensionIsOneOf(path, []string{ExtensionJson}):
return `.d.json.ts` // Drive-by redefinition of json declaration file output name so if it's ever enabled, it behaves well
default:
return ExtensionDts
}
}
// changeAnyExtension changes the extension of a path to the provided extension if it has one of the provided extensions.
//
// changeAnyExtension("/path/to/file.ext", ".js", ".ext") === "/path/to/file.js"
// changeAnyExtension("/path/to/file.ext", ".js", ".ts") === "/path/to/file.ext"
// changeAnyExtension("/path/to/file.ext", ".js", [".ext", ".ts"]) === "/path/to/file.js"
func changeAnyExtension(path string, ext string, extensions []string, ignoreCase bool) string {
pathext := GetAnyExtensionFromPath(path, extensions, ignoreCase)
if pathext != "" {
result := path[:len(path)-len(pathext)]
if strings.HasPrefix(ext, ".") {
return result + ext
} else {
return result + "." + ext
}
}
return path
}
func ChangeExtension(path string, newExtension string) string {
return changeAnyExtension(path, newExtension, extensionsToRemove /*ignoreCase*/, false)
}
// Like `changeAnyExtension`, but declaration file extensions are recognized
// and replaced starting from the `.d`.
//
// changeAnyExtension("file.d.ts", ".js") === "file.d.js"
// changeFullExtension("file.d.ts", ".js") === "file.js"
func ChangeFullExtension(path string, newExtension string) string {
declarationExtension := GetDeclarationFileExtension(path)
if declarationExtension != "" {
ext := newExtension
if !strings.HasPrefix(ext, ".") {
ext = "." + ext
}
return path[:len(path)-len(declarationExtension)] + ext
}
return ChangeExtension(path, newExtension)
}