2025-10-15 10:12:44 +03:00

149 lines
4.2 KiB
Go

package packagejson
import (
"sync"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/semver"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
)
var typeScriptVersion = semver.MustParse(core.Version())
type PackageJson struct {
Fields
versionPaths VersionPaths
versionTraces []string
once sync.Once
}
func (p *PackageJson) GetVersionPaths(trace func(string)) VersionPaths {
p.once.Do(func() {
if p.Fields.TypesVersions.Type == JSONValueTypeNotPresent {
p.versionTraces = append(p.versionTraces, diagnostics.X_package_json_does_not_have_a_0_field.Format("typesVersions"))
return
}
if p.Fields.TypesVersions.Type != JSONValueTypeObject {
p.versionTraces = append(p.versionTraces, diagnostics.Expected_type_of_0_field_in_package_json_to_be_1_got_2.Format("typesVersions", "object", p.Fields.TypesVersions.Type.String()))
return
}
p.versionTraces = append(p.versionTraces, diagnostics.X_package_json_has_a_typesVersions_field_with_version_specific_path_mappings.Format("typesVersions"))
for key, value := range p.Fields.TypesVersions.AsObject().Entries() {
keyRange, ok := semver.TryParseVersionRange(key)
if !ok {
p.versionTraces = append(p.versionTraces, diagnostics.X_package_json_has_a_typesVersions_entry_0_that_is_not_a_valid_semver_range.Format(key))
continue
}
if keyRange.Test(&typeScriptVersion) {
if value.Type != JSONValueTypeObject {
p.versionTraces = append(p.versionTraces, diagnostics.Expected_type_of_0_field_in_package_json_to_be_1_got_2.Format("typesVersions['"+key+"']", "object", value.Type.String()))
return
}
p.versionPaths = VersionPaths{
Version: key,
pathsJSON: value.AsObject(),
}
return
}
}
p.versionTraces = append(p.versionTraces, diagnostics.X_package_json_does_not_have_a_typesVersions_entry_that_matches_version_0.Format(core.VersionMajorMinor()))
})
if trace != nil {
for _, msg := range p.versionTraces {
trace(msg)
}
}
return p.versionPaths
}
type VersionPaths struct {
Version string
pathsJSON *collections.OrderedMap[string, JSONValue]
paths *collections.OrderedMap[string, []string]
}
func (v *VersionPaths) Exists() bool {
return v != nil && v.Version != "" && v.pathsJSON != nil
}
func (v *VersionPaths) GetPaths() *collections.OrderedMap[string, []string] {
if !v.Exists() {
return nil
}
if v.paths != nil {
return v.paths
}
paths := collections.NewOrderedMapWithSizeHint[string, []string](v.pathsJSON.Size())
for key, value := range v.pathsJSON.Entries() {
if value.Type != JSONValueTypeArray {
continue
}
slice := make([]string, len(value.AsArray()))
for i, path := range value.AsArray() {
if path.Type != JSONValueTypeString {
continue
}
slice[i] = path.Value.(string)
}
paths.Set(key, slice)
}
v.paths = paths
return v.paths
}
type InfoCacheEntry struct {
PackageDirectory string
DirectoryExists bool
Contents *PackageJson
}
func (p *InfoCacheEntry) Exists() bool {
return p != nil && p.Contents != nil
}
func (p *InfoCacheEntry) GetContents() *PackageJson {
if p == nil || p.Contents == nil {
return nil
}
return p.Contents
}
func (p *InfoCacheEntry) GetDirectory() string {
if p == nil {
return ""
}
return p.PackageDirectory
}
type InfoCache struct {
cache collections.SyncMap[tspath.Path, *InfoCacheEntry]
currentDirectory string
useCaseSensitiveFileNames bool
}
func NewInfoCache(currentDirectory string, useCaseSensitiveFileNames bool) *InfoCache {
return &InfoCache{
currentDirectory: currentDirectory,
useCaseSensitiveFileNames: useCaseSensitiveFileNames,
}
}
func (p *InfoCache) Get(packageJsonPath string) *InfoCacheEntry {
key := tspath.ToPath(packageJsonPath, p.currentDirectory, p.useCaseSensitiveFileNames)
if value, ok := p.cache.Load(key); ok {
return value
}
return nil
}
func (p *InfoCache) Set(packageJsonPath string, info *InfoCacheEntry) *InfoCacheEntry {
key := tspath.ToPath(packageJsonPath, p.currentDirectory, p.useCaseSensitiveFileNames)
actual, _ := p.cache.LoadOrStore(key, info)
return actual
}