Skip to content

Commit c7288d7

Browse files
authored
Use ordered maps for options and users (#370)
1 parent 4fcbc64 commit c7288d7

File tree

10 files changed

+288
-207
lines changed

10 files changed

+288
-207
lines changed

internal/collections/ordered_map.go

+75
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ package collections
22

33
import (
44
"bytes"
5+
"encoding"
56
"encoding/json"
67
"errors"
78
"fmt"
89
"iter"
910
"maps"
1011
"reflect"
1112
"slices"
13+
"strconv"
1214

1315
json2 "github.com./go-json-experiment/json"
1416
"github.com./go-json-experiment/json/jsontext"
@@ -105,6 +107,10 @@ func (m *OrderedMap[K, V]) Delete(key K) (V, bool) {
105107
// A slice of the keys can be obtained by calling `slices.Collect`.
106108
func (m *OrderedMap[K, V]) Keys() iter.Seq[K] {
107109
return func(yield func(K) bool) {
110+
if m == nil {
111+
return
112+
}
113+
108114
// We use a for loop here to ensure we enumerate new items added during iteration.
109115
//nolint:intrange
110116
for i := 0; i < len(m.keys); i++ {
@@ -119,6 +125,10 @@ func (m *OrderedMap[K, V]) Keys() iter.Seq[K] {
119125
// A slice of the values can be obtained by calling `slices.Collect`.
120126
func (m *OrderedMap[K, V]) Values() iter.Seq[V] {
121127
return func(yield func(V) bool) {
128+
if m == nil {
129+
return
130+
}
131+
122132
// We use a for loop here to ensure we enumerate new items added during iteration.
123133
//nolint:intrange
124134
for i := 0; i < len(m.keys); i++ {
@@ -132,6 +142,10 @@ func (m *OrderedMap[K, V]) Values() iter.Seq[V] {
132142
// Entries returns an iterator over the key-value pairs in the map.
133143
func (m *OrderedMap[K, V]) Entries() iter.Seq2[K, V] {
134144
return func(yield func(K, V) bool) {
145+
if m == nil {
146+
return
147+
}
148+
135149
// We use a for loop here to ensure we enumerate new items added during iteration.
136150
//nolint:intrange
137151
for i := 0; i < len(m.keys); i++ {
@@ -153,11 +167,19 @@ func (m *OrderedMap[K, V]) Clear() {
153167

154168
// Size returns the number of key-value pairs in the map.
155169
func (m *OrderedMap[K, V]) Size() int {
170+
if m == nil {
171+
return 0
172+
}
173+
156174
return len(m.keys)
157175
}
158176

159177
// Clone returns a shallow copy of the map.
160178
func (m *OrderedMap[K, V]) Clone() *OrderedMap[K, V] {
179+
if m == nil {
180+
return nil
181+
}
182+
161183
m2 := m.clone()
162184
return &m2
163185
}
@@ -169,6 +191,59 @@ func (m *OrderedMap[K, V]) clone() OrderedMap[K, V] {
169191
}
170192
}
171193

194+
func (m OrderedMap[K, V]) MarshalJSON() ([]byte, error) {
195+
if len(m.mp) == 0 {
196+
return []byte("{}"), nil
197+
}
198+
var buf bytes.Buffer
199+
buf.WriteByte('{')
200+
enc := json.NewEncoder(&buf)
201+
enc.SetEscapeHTML(false)
202+
203+
for i, k := range m.keys {
204+
if i > 0 {
205+
buf.WriteByte(',')
206+
}
207+
208+
keyString, err := resolveKeyName(reflect.ValueOf(k))
209+
if err != nil {
210+
return nil, err
211+
}
212+
213+
if err := enc.Encode(keyString); err != nil {
214+
return nil, err
215+
}
216+
217+
buf.WriteByte(':')
218+
219+
if err := enc.Encode(m.mp[k]); err != nil {
220+
return nil, err
221+
}
222+
}
223+
buf.WriteByte('}')
224+
return buf.Bytes(), nil
225+
}
226+
227+
func resolveKeyName(k reflect.Value) (string, error) {
228+
if k.Kind() == reflect.String {
229+
return k.String(), nil
230+
}
231+
if tm, ok := k.Interface().(encoding.TextMarshaler); ok {
232+
if k.Kind() == reflect.Pointer && k.IsNil() {
233+
return "", nil
234+
}
235+
buf, err := tm.MarshalText()
236+
return string(buf), err
237+
}
238+
switch k.Kind() {
239+
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
240+
return strconv.FormatInt(k.Int(), 10), nil
241+
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
242+
return strconv.FormatUint(k.Uint(), 10), nil
243+
}
244+
panic("unexpected map key type")
245+
}
246+
172247
func (m *OrderedMap[K, V]) UnmarshalJSON(data []byte) error {
173248
if string(data) == "null" {
174249
// By convention, to approximate the behavior of Unmarshal itself,

internal/compiler/module/resolver.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -1023,7 +1023,7 @@ func (r *resolutionState) tryLoadModuleUsingOptionalResolutionSettings() *resolv
10231023
}
10241024

10251025
func (r *resolutionState) tryLoadModuleUsingPathsIfEligible() *resolved {
1026-
if len(r.compilerOptions.Paths) > 0 && !tspath.PathIsRelative(r.name) {
1026+
if r.compilerOptions.Paths.Size() > 0 && !tspath.PathIsRelative(r.name) {
10271027
if r.resolver.traceEnabled() {
10281028
r.resolver.host.Trace(diagnostics.X_paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0.Format(r.name))
10291029
}
@@ -1045,13 +1045,13 @@ func (r *resolutionState) tryLoadModuleUsingPathsIfEligible() *resolved {
10451045
)
10461046
}
10471047

1048-
func (r *resolutionState) tryLoadModuleUsingPaths(extensions extensions, moduleName string, containingDirectory string, paths map[string][]string, pathPatterns parsedPatterns, loader resolutionKindSpecificLoader, onlyRecordFailures bool) *resolved {
1048+
func (r *resolutionState) tryLoadModuleUsingPaths(extensions extensions, moduleName string, containingDirectory string, paths *collections.OrderedMap[string, []string], pathPatterns parsedPatterns, loader resolutionKindSpecificLoader, onlyRecordFailures bool) *resolved {
10491049
if matchedPattern := matchPatternOrExact(pathPatterns, moduleName); matchedPattern.IsValid() {
10501050
matchedStar := matchedPattern.MatchedText(moduleName)
10511051
if r.resolver.traceEnabled() {
10521052
r.resolver.host.Trace(diagnostics.Module_name_0_matched_pattern_1.Format(moduleName, matchedPattern.Text))
10531053
}
1054-
for _, subst := range paths[matchedPattern.Text] {
1054+
for _, subst := range paths.GetOrZero(matchedPattern.Text) {
10551055
path := strings.Replace(subst, "*", matchedStar, 1)
10561056
candidate := tspath.NormalizePath(tspath.CombinePaths(containingDirectory, path))
10571057
if r.resolver.traceEnabled() {
@@ -1688,7 +1688,7 @@ func moveToNextDirectorySeparatorIfAvailable(path string, prevSeparatorIndex int
16881688
}
16891689

16901690
func getPathsBasePath(options *core.CompilerOptions, currentDirectory string) string {
1691-
if len(options.Paths) == 0 {
1691+
if options.Paths.Size() == 0 {
16921692
return ""
16931693
}
16941694
if options.PathsBasePath != "" {
@@ -1702,12 +1702,12 @@ type parsedPatterns struct {
17021702
patterns []core.Pattern
17031703
}
17041704

1705-
func tryParsePatterns(paths map[string][]string) parsedPatterns {
1705+
func tryParsePatterns(paths *collections.OrderedMap[string, []string]) parsedPatterns {
17061706
// !!! TS has a weakmap cache
17071707
// We could store a cache on Resolver, but maybe we can wait and profile
17081708
matchableStringSet := collections.OrderedSet[string]{}
1709-
patterns := make([]core.Pattern, 0, len(paths))
1710-
for path := range paths {
1709+
patterns := make([]core.Pattern, 0, paths.Size())
1710+
for path := range paths.Keys() {
17111711
if pattern := core.TryParsePattern(path); pattern.IsValid() {
17121712
if pattern.StarIndex == -1 {
17131713
matchableStringSet.Add(path)

internal/compiler/packagejson/cache.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -70,33 +70,35 @@ func (p *PackageJson) GetVersionPaths(trace func(string)) VersionPaths {
7070
type VersionPaths struct {
7171
Version string
7272
pathsJSON *collections.OrderedMap[string, JSONValue]
73-
paths map[string][]string
73+
paths *collections.OrderedMap[string, []string]
7474
}
7575

7676
func (v *VersionPaths) Exists() bool {
7777
return v != nil && v.Version != "" && v.pathsJSON != nil
7878
}
7979

80-
func (v *VersionPaths) GetPaths() map[string][]string {
80+
func (v *VersionPaths) GetPaths() *collections.OrderedMap[string, []string] {
8181
if !v.Exists() {
8282
return nil
8383
}
8484
if v.paths != nil {
8585
return v.paths
8686
}
87-
v.paths = make(map[string][]string, v.pathsJSON.Size())
87+
paths := collections.NewOrderedMapWithSizeHint[string, []string](v.pathsJSON.Size())
8888
for key, value := range v.pathsJSON.Entries() {
8989
if value.Type != JSONValueTypeArray {
9090
continue
9191
}
92-
v.paths[key] = make([]string, len(value.AsArray()))
92+
slice := make([]string, len(value.AsArray()))
9393
for i, path := range value.AsArray() {
9494
if path.Type != JSONValueTypeString {
9595
continue
9696
}
97-
v.paths[key][i] = path.Value.(string)
97+
slice[i] = path.Value.(string)
9898
}
99+
v.paths.Set(key, slice)
99100
}
101+
v.paths = paths
100102
return v.paths
101103
}
102104

0 commit comments

Comments
 (0)