goscript 0.0.38 → 0.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/analysis.go +15 -6
- package/compiler/compiler.go +184 -34
- package/compiler/expr-call.go +19 -9
- package/compiler/field.go +17 -3
- package/compiler/gs_dependencies_test.go +80 -0
- package/compiler/lit.go +12 -6
- package/compiler/output.go +10 -4
- package/compiler/spec.go +15 -2
- package/compiler/type-assert.go +111 -21
- package/compiler/type.go +37 -8
- package/dist/gs/builtin/builtin.d.ts +55 -0
- package/dist/gs/builtin/builtin.js +213 -0
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/slice.js +13 -0
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/bytes/buffer.gs.d.ts +56 -0
- package/dist/gs/bytes/buffer.gs.js +611 -0
- package/dist/gs/bytes/buffer.gs.js.map +1 -0
- package/dist/gs/bytes/bytes.gs.d.ts +78 -0
- package/dist/gs/bytes/bytes.gs.js +1107 -0
- package/dist/gs/bytes/bytes.gs.js.map +1 -0
- package/dist/gs/bytes/index.d.ts +4 -0
- package/dist/gs/bytes/index.js +5 -0
- package/dist/gs/bytes/index.js.map +1 -0
- package/dist/gs/bytes/iter.gs.d.ts +9 -0
- package/dist/gs/bytes/iter.gs.js +143 -0
- package/dist/gs/bytes/iter.gs.js.map +1 -0
- package/dist/gs/bytes/reader.gs.d.ts +34 -0
- package/dist/gs/bytes/reader.gs.js +198 -0
- package/dist/gs/bytes/reader.gs.js.map +1 -0
- package/dist/gs/fmt/fmt.d.ts +49 -0
- package/dist/gs/fmt/fmt.js +322 -0
- package/dist/gs/fmt/fmt.js.map +1 -0
- package/dist/gs/fmt/index.d.ts +1 -0
- package/dist/gs/fmt/index.js +2 -0
- package/dist/gs/fmt/index.js.map +1 -0
- package/dist/gs/internal/bytealg/index.d.ts +14 -2
- package/dist/gs/internal/bytealg/index.js +114 -8
- package/dist/gs/internal/bytealg/index.js.map +1 -1
- package/dist/gs/path/filepath/index.d.ts +3 -0
- package/dist/gs/path/filepath/index.js +3 -0
- package/dist/gs/path/filepath/index.js.map +1 -0
- package/dist/gs/path/filepath/match.d.ts +3 -0
- package/dist/gs/path/filepath/match.js +212 -0
- package/dist/gs/path/filepath/match.js.map +1 -0
- package/dist/gs/path/filepath/path.d.ts +25 -0
- package/dist/gs/path/filepath/path.js +265 -0
- package/dist/gs/path/filepath/path.js.map +1 -0
- package/dist/gs/reflect/deepequal.d.ts +2 -1
- package/dist/gs/reflect/deepequal.js +5 -53
- package/dist/gs/reflect/deepequal.js.map +1 -1
- package/dist/gs/reflect/map.d.ts +14 -8
- package/dist/gs/reflect/map.js +15 -11
- package/dist/gs/reflect/map.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +17 -9
- package/dist/gs/reflect/type.js +1 -1
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/reflect/value.js +15 -6
- package/dist/gs/reflect/value.js.map +1 -1
- package/dist/gs/reflect/visiblefields.js +18 -12
- package/dist/gs/reflect/visiblefields.js.map +1 -1
- package/dist/gs/sort/index.d.ts +4 -0
- package/dist/gs/sort/index.js +4 -0
- package/dist/gs/sort/index.js.map +1 -0
- package/dist/gs/sort/search.gs.d.ts +6 -0
- package/dist/gs/sort/search.gs.js +125 -0
- package/dist/gs/sort/search.gs.js.map +1 -0
- package/dist/gs/sort/slice.gs.d.ts +4 -0
- package/dist/gs/sort/slice.gs.js +49 -0
- package/dist/gs/sort/slice.gs.js.map +1 -0
- package/dist/gs/sort/sort.gs.d.ts +37 -0
- package/dist/gs/sort/sort.gs.js +203 -0
- package/dist/gs/sort/sort.gs.js.map +1 -0
- package/dist/gs/unicode/utf8/utf8.d.ts +1 -1
- package/dist/gs/unicode/utf8/utf8.js +4 -2
- package/dist/gs/unicode/utf8/utf8.js.map +1 -1
- package/gs/builtin/builtin.ts +236 -0
- package/gs/builtin/slice.ts +17 -1
- package/gs/bytes/buffer.gs.ts +614 -0
- package/gs/bytes/bytes.gs.ts +1288 -0
- package/gs/bytes/godoc.txt +69 -0
- package/gs/bytes/index.ts +69 -0
- package/gs/bytes/iter.gs.ts +149 -0
- package/gs/bytes/metadata.go +12 -0
- package/gs/bytes/reader.gs.ts +230 -0
- package/gs/fmt/fmt.ts +407 -0
- package/gs/fmt/godoc.txt +382 -0
- package/gs/fmt/index.ts +31 -0
- package/gs/fmt/metadata.go +7 -0
- package/gs/internal/bytealg/index.ts +125 -10
- package/gs/internal/metadata.go +7 -0
- package/gs/io/metadata.go +11 -0
- package/gs/maps/metadata.go +8 -0
- package/gs/math/metadata.go +7 -0
- package/gs/os/metadata.go +17 -0
- package/gs/path/filepath/godoc.txt +35 -0
- package/gs/path/filepath/index.ts +27 -0
- package/gs/path/filepath/match.test.ts +274 -0
- package/gs/path/filepath/match.ts +249 -0
- package/gs/path/filepath/path.test.ts +246 -0
- package/gs/path/filepath/path.ts +328 -0
- package/gs/path/metadata.go +8 -0
- package/gs/reflect/deepequal.test.ts +41 -0
- package/gs/reflect/deepequal.ts +19 -4
- package/gs/reflect/map.test.ts +30 -0
- package/gs/reflect/map.ts +22 -18
- package/gs/reflect/metadata.go +7 -0
- package/gs/reflect/type.ts +19 -15
- package/gs/reflect/value.ts +21 -7
- package/gs/reflect/visiblefields.ts +17 -13
- package/gs/sort/godoc.txt +27 -0
- package/gs/sort/index.ts +24 -0
- package/gs/sort/search.gs.ts +128 -0
- package/gs/sort/slice.gs.ts +59 -0
- package/gs/sort/sort.gs.ts +227 -0
- package/gs/strconv/metadata.go +7 -0
- package/gs/strings/metadata.go +11 -0
- package/gs/sync/metadata.go +7 -0
- package/gs/unicode/utf8/utf8.ts +8 -5
- package/package.json +1 -1
- package/dist/gs/internal/testlog/index.d.ts +0 -1
- package/dist/gs/internal/testlog/index.js +0 -5
- package/dist/gs/internal/testlog/index.js.map +0 -1
- package/dist/gs/maps/iter.gs.d.ts +0 -7
- package/dist/gs/maps/iter.gs.js +0 -65
- package/dist/gs/maps/iter.gs.js.map +0 -1
- package/dist/gs/maps/maps.gs.d.ts +0 -7
- package/dist/gs/maps/maps.gs.js +0 -79
- package/dist/gs/maps/maps.gs.js.map +0 -1
- package/dist/gs/reflect/abi.d.ts +0 -59
- package/dist/gs/reflect/abi.gs.d.ts +0 -59
- package/dist/gs/reflect/abi.gs.js +0 -79
- package/dist/gs/reflect/abi.gs.js.map +0 -1
- package/dist/gs/reflect/abi.js +0 -79
- package/dist/gs/reflect/abi.js.map +0 -1
- package/dist/gs/reflect/badlinkname.d.ts +0 -52
- package/dist/gs/reflect/badlinkname.gs.d.ts +0 -52
- package/dist/gs/reflect/badlinkname.gs.js +0 -72
- package/dist/gs/reflect/badlinkname.gs.js.map +0 -1
- package/dist/gs/reflect/badlinkname.js +0 -72
- package/dist/gs/reflect/badlinkname.js.map +0 -1
- package/dist/gs/reflect/deepequal.gs.d.ts +0 -25
- package/dist/gs/reflect/deepequal.gs.js +0 -308
- package/dist/gs/reflect/deepequal.gs.js.map +0 -1
- package/dist/gs/reflect/float32reg_generic.gs.d.ts +0 -2
- package/dist/gs/reflect/float32reg_generic.gs.js +0 -10
- package/dist/gs/reflect/float32reg_generic.gs.js.map +0 -1
- package/dist/gs/reflect/index.gs.d.ts +0 -1
- package/dist/gs/reflect/index.gs.js +0 -3
- package/dist/gs/reflect/index.gs.js.map +0 -1
- package/dist/gs/reflect/iter.gs.d.ts +0 -3
- package/dist/gs/reflect/iter.gs.js +0 -24
- package/dist/gs/reflect/iter.gs.js.map +0 -1
- package/dist/gs/reflect/makefunc.gs.d.ts +0 -34
- package/dist/gs/reflect/makefunc.gs.js +0 -288
- package/dist/gs/reflect/makefunc.gs.js.map +0 -1
- package/dist/gs/reflect/map_swiss.gs.d.ts +0 -14
- package/dist/gs/reflect/map_swiss.gs.js +0 -70
- package/dist/gs/reflect/map_swiss.gs.js.map +0 -1
- package/dist/gs/reflect/reflect.gs.d.ts +0 -132
- package/dist/gs/reflect/reflect.gs.js +0 -437
- package/dist/gs/reflect/reflect.gs.js.map +0 -1
- package/dist/gs/reflect/swapper.gs.d.ts +0 -1
- package/dist/gs/reflect/swapper.gs.js +0 -32
- package/dist/gs/reflect/swapper.gs.js.map +0 -1
- package/dist/gs/reflect/type.gs.d.ts +0 -4
- package/dist/gs/reflect/type.gs.js +0 -21
- package/dist/gs/reflect/type.gs.js.map +0 -1
- package/dist/gs/reflect/value.gs.d.ts +0 -4
- package/dist/gs/reflect/value.gs.js +0 -12
- package/dist/gs/reflect/value.gs.js.map +0 -1
- package/dist/gs/reflect/visiblefields.gs.d.ts +0 -3
- package/dist/gs/reflect/visiblefields.gs.js +0 -123
- package/dist/gs/reflect/visiblefields.gs.js.map +0 -1
- package/dist/gs/stringslite/index.d.ts +0 -1
- package/dist/gs/stringslite/index.js +0 -2
- package/dist/gs/stringslite/index.js.map +0 -1
- package/dist/gs/stringslite/strings.d.ts +0 -11
- package/dist/gs/stringslite/strings.js +0 -67
- package/dist/gs/stringslite/strings.js.map +0 -1
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
Base,
|
|
4
|
+
Dir,
|
|
5
|
+
Ext,
|
|
6
|
+
Clean,
|
|
7
|
+
Join,
|
|
8
|
+
Split,
|
|
9
|
+
IsAbs,
|
|
10
|
+
ToSlash,
|
|
11
|
+
FromSlash,
|
|
12
|
+
VolumeName,
|
|
13
|
+
IsLocal,
|
|
14
|
+
SplitList,
|
|
15
|
+
HasPrefix,
|
|
16
|
+
Abs,
|
|
17
|
+
Rel,
|
|
18
|
+
EvalSymlinks,
|
|
19
|
+
Separator,
|
|
20
|
+
ListSeparator,
|
|
21
|
+
} from './path.js'
|
|
22
|
+
|
|
23
|
+
describe('path/filepath - Path manipulation functions', () => {
|
|
24
|
+
describe('Base', () => {
|
|
25
|
+
it('should return the last element of path', () => {
|
|
26
|
+
expect(Base('dir/subdir/file.txt')).toBe('file.txt')
|
|
27
|
+
expect(Base('/usr/bin/ls')).toBe('ls')
|
|
28
|
+
expect(Base('file.txt')).toBe('file.txt')
|
|
29
|
+
expect(Base('/')).toBe('/')
|
|
30
|
+
expect(Base('')).toBe('.')
|
|
31
|
+
expect(Base('//')).toBe('/')
|
|
32
|
+
expect(Base('dir/')).toBe('dir')
|
|
33
|
+
expect(Base('dir//')).toBe('dir')
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('Dir', () => {
|
|
38
|
+
it('should return directory portion of path', () => {
|
|
39
|
+
expect(Dir('dir/subdir/file.txt')).toBe('dir/subdir')
|
|
40
|
+
expect(Dir('/usr/bin/ls')).toBe('/usr/bin')
|
|
41
|
+
expect(Dir('file.txt')).toBe('.')
|
|
42
|
+
expect(Dir('/')).toBe('/')
|
|
43
|
+
expect(Dir('')).toBe('.')
|
|
44
|
+
expect(Dir('/file')).toBe('/')
|
|
45
|
+
expect(Dir('dir/')).toBe('.')
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe('Ext', () => {
|
|
50
|
+
it('should return file extension', () => {
|
|
51
|
+
expect(Ext('file.txt')).toBe('.txt')
|
|
52
|
+
expect(Ext('file.tar.gz')).toBe('.gz')
|
|
53
|
+
expect(Ext('file')).toBe('')
|
|
54
|
+
expect(Ext('.hidden')).toBe('')
|
|
55
|
+
expect(Ext('dir/file.txt')).toBe('.txt')
|
|
56
|
+
expect(Ext('')).toBe('')
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('Clean', () => {
|
|
61
|
+
it('should clean up path by resolving . and .. elements', () => {
|
|
62
|
+
expect(Clean('dir//subdir/../subdir/./file.txt')).toBe(
|
|
63
|
+
'dir/subdir/file.txt',
|
|
64
|
+
)
|
|
65
|
+
expect(Clean('/dir/../file')).toBe('/file')
|
|
66
|
+
expect(Clean('./file')).toBe('file')
|
|
67
|
+
expect(Clean('../file')).toBe('../file')
|
|
68
|
+
expect(Clean('dir/..')).toBe('.')
|
|
69
|
+
expect(Clean('/dir/..')).toBe('/')
|
|
70
|
+
expect(Clean('')).toBe('.')
|
|
71
|
+
expect(Clean('/')).toBe('/')
|
|
72
|
+
expect(Clean('///')).toBe('/')
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
describe('Join', () => {
|
|
77
|
+
it('should join path elements with separator', () => {
|
|
78
|
+
expect(Join('dir', 'subdir', 'file.txt')).toBe('dir/subdir/file.txt')
|
|
79
|
+
expect(Join('/usr', 'bin', 'ls')).toBe('/usr/bin/ls')
|
|
80
|
+
expect(Join('', 'file')).toBe('file')
|
|
81
|
+
expect(Join('dir', '', 'file')).toBe('dir/file')
|
|
82
|
+
expect(Join()).toBe('')
|
|
83
|
+
expect(Join('', '', '')).toBe('')
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
describe('Split', () => {
|
|
88
|
+
it('should split path into directory and file', () => {
|
|
89
|
+
expect(Split('dir/subdir/file.txt')).toEqual(['dir/subdir/', 'file.txt'])
|
|
90
|
+
expect(Split('/usr/bin/ls')).toEqual(['/usr/bin/', 'ls'])
|
|
91
|
+
expect(Split('file.txt')).toEqual(['', 'file.txt'])
|
|
92
|
+
expect(Split('/')).toEqual(['/', ''])
|
|
93
|
+
expect(Split('')).toEqual(['', ''])
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
describe('IsAbs', () => {
|
|
98
|
+
it('should check if path is absolute', () => {
|
|
99
|
+
expect(IsAbs('/absolute/path')).toBe(true)
|
|
100
|
+
expect(IsAbs('relative/path')).toBe(false)
|
|
101
|
+
expect(IsAbs('/')).toBe(true)
|
|
102
|
+
expect(IsAbs('')).toBe(false)
|
|
103
|
+
expect(IsAbs('./relative')).toBe(false)
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
describe('ToSlash', () => {
|
|
108
|
+
it('should preserve path on Unix systems (backslashes are regular chars)', () => {
|
|
109
|
+
// On Unix systems, ToSlash doesn't convert backslashes because they're not separators
|
|
110
|
+
expect(ToSlash('dir\\subdir\\file.txt')).toBe('dir\\subdir\\file.txt')
|
|
111
|
+
expect(ToSlash('dir/subdir/file.txt')).toBe('dir/subdir/file.txt')
|
|
112
|
+
expect(ToSlash('')).toBe('')
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
describe('FromSlash', () => {
|
|
117
|
+
it('should preserve path on Unix systems', () => {
|
|
118
|
+
// On Unix systems, FromSlash doesn't change anything because separator is already '/'
|
|
119
|
+
expect(FromSlash('dir/subdir/file.txt')).toBe('dir/subdir/file.txt')
|
|
120
|
+
expect(FromSlash('')).toBe('')
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
describe('VolumeName', () => {
|
|
125
|
+
it('should return empty string on Unix systems', () => {
|
|
126
|
+
expect(VolumeName('C:\\Windows\\System32')).toBe('')
|
|
127
|
+
expect(VolumeName('/usr/local')).toBe('')
|
|
128
|
+
expect(VolumeName('')).toBe('')
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
describe('IsLocal', () => {
|
|
133
|
+
it('should check if path is local (not escaping)', () => {
|
|
134
|
+
expect(IsLocal('file.txt')).toBe(true)
|
|
135
|
+
expect(IsLocal('dir/file.txt')).toBe(true)
|
|
136
|
+
expect(IsLocal('../file.txt')).toBe(false)
|
|
137
|
+
expect(IsLocal('/absolute/path')).toBe(false)
|
|
138
|
+
expect(IsLocal('')).toBe(false)
|
|
139
|
+
expect(IsLocal('dir/../file')).toBe(true)
|
|
140
|
+
expect(IsLocal('dir/../../file')).toBe(false)
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
describe('SplitList', () => {
|
|
145
|
+
it('should split PATH-style lists', () => {
|
|
146
|
+
expect(SplitList('/usr/bin:/usr/local/bin:/bin')).toEqual([
|
|
147
|
+
'/usr/bin',
|
|
148
|
+
'/usr/local/bin',
|
|
149
|
+
'/bin',
|
|
150
|
+
])
|
|
151
|
+
expect(SplitList('')).toEqual([])
|
|
152
|
+
expect(SplitList('/single/path')).toEqual(['/single/path'])
|
|
153
|
+
expect(SplitList('a:b:c')).toEqual(['a', 'b', 'c'])
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
describe('HasPrefix', () => {
|
|
158
|
+
it('should check if path has prefix', () => {
|
|
159
|
+
expect(HasPrefix('/usr/local/bin', '/usr/local')).toBe(true)
|
|
160
|
+
expect(HasPrefix('/usr/local', '/usr/local')).toBe(true)
|
|
161
|
+
expect(HasPrefix('/usr/local/bin', '/usr/bin')).toBe(false)
|
|
162
|
+
expect(HasPrefix('relative/path', 'relative')).toBe(true)
|
|
163
|
+
expect(HasPrefix('file.txt', '')).toBe(true)
|
|
164
|
+
expect(HasPrefix('', 'prefix')).toBe(false)
|
|
165
|
+
})
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
describe('Abs', () => {
|
|
169
|
+
it('should handle absolute paths', () => {
|
|
170
|
+
const [result1, err1] = Abs('/absolute/path')
|
|
171
|
+
expect(err1).toBeNull()
|
|
172
|
+
expect(result1).toBe('/absolute/path')
|
|
173
|
+
|
|
174
|
+
const [result2, err2] = Abs('relative/path')
|
|
175
|
+
expect(err2).toBeNull()
|
|
176
|
+
expect(result2).toBe('/relative/path')
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
describe('Rel', () => {
|
|
181
|
+
it('should calculate relative path', () => {
|
|
182
|
+
const [result1, err1] = Rel('/usr/local', '/usr/local')
|
|
183
|
+
expect(err1).toBeNull()
|
|
184
|
+
expect(result1).toBe('.')
|
|
185
|
+
|
|
186
|
+
const [result2, err2] = Rel('/usr/local', '/usr/local/bin')
|
|
187
|
+
expect(err2).toBeNull()
|
|
188
|
+
expect(result2).toBe('bin')
|
|
189
|
+
|
|
190
|
+
const [result3, err3] = Rel('/usr/local', '/other/path')
|
|
191
|
+
expect(err3).toBeNull()
|
|
192
|
+
expect(result3).toBe('/other/path')
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
describe('EvalSymlinks', () => {
|
|
197
|
+
it('should return cleaned path (stubbed)', () => {
|
|
198
|
+
const [result, err] = EvalSymlinks('/path/with/../dots')
|
|
199
|
+
expect(err).toBeNull()
|
|
200
|
+
expect(result).toBe('/path/dots')
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
describe('Constants', () => {
|
|
205
|
+
it('should have correct separator constants', () => {
|
|
206
|
+
expect(Separator).toBe('/')
|
|
207
|
+
expect(ListSeparator).toBe(':')
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
describe('Complex path operations', () => {
|
|
213
|
+
it('should handle edge cases correctly', () => {
|
|
214
|
+
// Test Clean with complex paths
|
|
215
|
+
expect(Clean('/a/b/../c/./d/')).toBe('/a/c/d')
|
|
216
|
+
expect(Clean('a/b/../../c')).toBe('c')
|
|
217
|
+
expect(Clean('../../a/b')).toBe('../../a/b')
|
|
218
|
+
|
|
219
|
+
// Test Join with various inputs
|
|
220
|
+
expect(Join('/a', '../b', 'c')).toBe('/b/c')
|
|
221
|
+
expect(Join('a', '/b', 'c')).toBe('/b/c')
|
|
222
|
+
|
|
223
|
+
// Test Split edge cases
|
|
224
|
+
expect(Split('/a/')).toEqual(['/a/', ''])
|
|
225
|
+
expect(Split('//a')).toEqual(['//', 'a'])
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('should maintain path consistency', () => {
|
|
229
|
+
const testPaths = [
|
|
230
|
+
'simple/path',
|
|
231
|
+
'/absolute/path',
|
|
232
|
+
'path/with/../dots',
|
|
233
|
+
'./relative/path',
|
|
234
|
+
'../../parent/path',
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
for (const path of testPaths) {
|
|
238
|
+
const cleaned = Clean(path)
|
|
239
|
+
const [dir, file] = Split(cleaned)
|
|
240
|
+
const rejoined = dir + file
|
|
241
|
+
|
|
242
|
+
// Split and rejoin should preserve the cleaned path
|
|
243
|
+
expect(rejoined).toBe(cleaned)
|
|
244
|
+
}
|
|
245
|
+
})
|
|
246
|
+
})
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
// Package filepath implements utility routines for manipulating filename paths
|
|
2
|
+
// in a way compatible with the target operating system-defined file paths.
|
|
3
|
+
|
|
4
|
+
// Path separator constants
|
|
5
|
+
export const Separator = '/'
|
|
6
|
+
export const ListSeparator = ':'
|
|
7
|
+
|
|
8
|
+
// Error constants
|
|
9
|
+
export const SkipDir = new Error('skip this directory')
|
|
10
|
+
export const SkipAll = new Error('skip everything and stop the walk')
|
|
11
|
+
|
|
12
|
+
// Base returns the last element of path.
|
|
13
|
+
// Trailing path separators are removed before extracting the last element.
|
|
14
|
+
// If the path is empty, Base returns ".".
|
|
15
|
+
// If the path consists entirely of separators, Base returns a single separator.
|
|
16
|
+
export function Base(path: string): string {
|
|
17
|
+
if (path === '') {
|
|
18
|
+
return '.'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Strip trailing slashes
|
|
22
|
+
path = path.replace(/\/+$/, '')
|
|
23
|
+
|
|
24
|
+
if (path === '') {
|
|
25
|
+
return '/'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Find the last slash
|
|
29
|
+
const i = path.lastIndexOf('/')
|
|
30
|
+
if (i >= 0) {
|
|
31
|
+
return path.substring(i + 1)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return path
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Dir returns all but the last element of path, typically the path's directory.
|
|
38
|
+
// After dropping the final element, Dir calls Clean on the path and trailing
|
|
39
|
+
// slashes are removed. If the path is empty, Dir returns ".".
|
|
40
|
+
// If the path consists entirely of separators, Dir returns a single separator.
|
|
41
|
+
export function Dir(path: string): string {
|
|
42
|
+
if (path === '') {
|
|
43
|
+
return '.'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Strip trailing slashes
|
|
47
|
+
path = path.replace(/\/+$/, '')
|
|
48
|
+
|
|
49
|
+
if (path === '') {
|
|
50
|
+
return '/'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Find the last slash
|
|
54
|
+
const i = path.lastIndexOf('/')
|
|
55
|
+
if (i >= 0) {
|
|
56
|
+
const dir = path.substring(0, i)
|
|
57
|
+
return Clean(dir === '' ? '/' : dir)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return '.'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Ext returns the file name extension used by path.
|
|
64
|
+
// The extension is the suffix beginning at the final dot
|
|
65
|
+
// in the final element of path; it is empty if there is no dot.
|
|
66
|
+
export function Ext(path: string): string {
|
|
67
|
+
const base = Base(path)
|
|
68
|
+
|
|
69
|
+
// Handle special case: if the base starts with a dot and has no other dots,
|
|
70
|
+
// it's a hidden file with no extension
|
|
71
|
+
if (base.startsWith('.') && base.indexOf('.', 1) === -1) {
|
|
72
|
+
return ''
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const i = base.lastIndexOf('.')
|
|
76
|
+
if (i >= 0) {
|
|
77
|
+
return base.substring(i)
|
|
78
|
+
}
|
|
79
|
+
return ''
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Clean returns the shortest path name equivalent to path
|
|
83
|
+
// by purely lexical processing.
|
|
84
|
+
export function Clean(path: string): string {
|
|
85
|
+
if (path === '') {
|
|
86
|
+
return '.'
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const isAbs = path.startsWith('/')
|
|
90
|
+
const segments = path
|
|
91
|
+
.split('/')
|
|
92
|
+
.filter((segment) => segment !== '' && segment !== '.')
|
|
93
|
+
const result: string[] = []
|
|
94
|
+
|
|
95
|
+
for (const segment of segments) {
|
|
96
|
+
if (segment === '..') {
|
|
97
|
+
if (result.length > 0 && result[result.length - 1] !== '..') {
|
|
98
|
+
result.pop()
|
|
99
|
+
} else if (!isAbs) {
|
|
100
|
+
result.push('..')
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
result.push(segment)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let cleaned = result.join('/')
|
|
108
|
+
if (isAbs) {
|
|
109
|
+
cleaned = '/' + cleaned
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
cleaned === '' ?
|
|
114
|
+
isAbs ? '/'
|
|
115
|
+
: '.'
|
|
116
|
+
: cleaned
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Join joins any number of path elements into a single path,
|
|
121
|
+
// separating them with an OS specific Separator. Empty elements
|
|
122
|
+
// are ignored. The result is Cleaned. However, if the argument
|
|
123
|
+
// list is empty or all its elements are empty, Join returns
|
|
124
|
+
// an empty string.
|
|
125
|
+
export function Join(...elem: string[]): string {
|
|
126
|
+
if (elem.length === 0) {
|
|
127
|
+
return ''
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Filter out empty elements but handle absolute paths
|
|
131
|
+
const parts: string[] = []
|
|
132
|
+
|
|
133
|
+
for (const e of elem) {
|
|
134
|
+
if (e === '') {
|
|
135
|
+
continue
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// If this element is absolute, start over from here
|
|
139
|
+
if (IsAbs(e)) {
|
|
140
|
+
parts.length = 0 // Clear previous parts
|
|
141
|
+
parts.push(e)
|
|
142
|
+
} else {
|
|
143
|
+
parts.push(e)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (parts.length === 0) {
|
|
148
|
+
return ''
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return Clean(parts.join('/'))
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Split splits path immediately following the final Separator,
|
|
155
|
+
// separating it into a directory and file name component.
|
|
156
|
+
// If there is no Separator in path, Split returns an empty dir
|
|
157
|
+
// and file set to path. The returned values have the property
|
|
158
|
+
// that path = dir+file.
|
|
159
|
+
export function Split(path: string): [string, string] {
|
|
160
|
+
const i = path.lastIndexOf('/')
|
|
161
|
+
if (i < 0) {
|
|
162
|
+
return ['', path]
|
|
163
|
+
}
|
|
164
|
+
return [path.substring(0, i + 1), path.substring(i + 1)]
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// IsAbs reports whether the path is absolute.
|
|
168
|
+
export function IsAbs(path: string): boolean {
|
|
169
|
+
return path.startsWith('/')
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ToSlash returns the result of replacing each separator character
|
|
173
|
+
// in path with a slash ('/') character. Multiple separators are
|
|
174
|
+
// replaced by multiple slashes.
|
|
175
|
+
export function ToSlash(path: string): string {
|
|
176
|
+
// On Unix-like systems (including our JS environment), the separator is already '/'
|
|
177
|
+
// so backslashes are just regular characters and should not be converted
|
|
178
|
+
// This matches Go's behavior on Unix systems
|
|
179
|
+
return path
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// FromSlash returns the result of replacing each slash ('/') character
|
|
183
|
+
// in path with a separator character. Multiple slashes are replaced
|
|
184
|
+
// by multiple separators.
|
|
185
|
+
export function FromSlash(path: string): string {
|
|
186
|
+
// On Unix-like systems (including our JS environment), separator is '/'
|
|
187
|
+
// so no conversion needed
|
|
188
|
+
return path
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// VolumeName returns leading volume name.
|
|
192
|
+
// Given "C:\foo\bar" it returns "C:" on Windows.
|
|
193
|
+
// Given "\\host\share\foo" it returns "\\host\share".
|
|
194
|
+
// On other systems, it returns "".
|
|
195
|
+
export function VolumeName(_path: string): string {
|
|
196
|
+
// In our JS environment, we don't have volume names
|
|
197
|
+
return ''
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// IsLocal reports whether path, using lexical analysis only,
|
|
201
|
+
// has all of these properties:
|
|
202
|
+
// - is within the subtree rooted at the directory in which path is evaluated
|
|
203
|
+
// - is not an absolute path
|
|
204
|
+
// - is not empty
|
|
205
|
+
// - on Windows, is not a reserved name such as "NUL"
|
|
206
|
+
export function IsLocal(path: string): boolean {
|
|
207
|
+
if (path === '' || IsAbs(path)) {
|
|
208
|
+
return false
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Check for .. components that would escape
|
|
212
|
+
const segments = path.split('/')
|
|
213
|
+
let depth = 0
|
|
214
|
+
|
|
215
|
+
for (const segment of segments) {
|
|
216
|
+
if (segment === '..') {
|
|
217
|
+
depth--
|
|
218
|
+
if (depth < 0) {
|
|
219
|
+
return false
|
|
220
|
+
}
|
|
221
|
+
} else if (segment !== '.' && segment !== '') {
|
|
222
|
+
depth++
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return true
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// SplitList splits a list of paths joined by the OS-specific ListSeparator,
|
|
230
|
+
// usually found in PATH or GOPATH environment variables.
|
|
231
|
+
// Unlike strings.Split, SplitList returns an empty slice when passed an empty string.
|
|
232
|
+
export function SplitList(path: string): string[] {
|
|
233
|
+
if (path === '') {
|
|
234
|
+
return []
|
|
235
|
+
}
|
|
236
|
+
return path.split(ListSeparator)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// HasPrefix tests whether the path p begins with prefix.
|
|
240
|
+
export function HasPrefix(p: string, prefix: string): boolean {
|
|
241
|
+
if (prefix === '') {
|
|
242
|
+
return true
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Normalize both paths
|
|
246
|
+
const normalP = Clean(p)
|
|
247
|
+
const normalPrefix = Clean(prefix)
|
|
248
|
+
|
|
249
|
+
if (normalP === normalPrefix) {
|
|
250
|
+
return true
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Check if p starts with prefix followed by a separator
|
|
254
|
+
if (normalP.startsWith(normalPrefix)) {
|
|
255
|
+
const remaining = normalP.substring(normalPrefix.length)
|
|
256
|
+
return remaining.startsWith('/')
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return false
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Stubs for functions that require filesystem operations
|
|
263
|
+
// These are simplified implementations for compatibility
|
|
264
|
+
|
|
265
|
+
export function Abs(path: string): [string, Error | null] {
|
|
266
|
+
if (IsAbs(path)) {
|
|
267
|
+
return [Clean(path), null]
|
|
268
|
+
}
|
|
269
|
+
// In a real implementation, this would resolve relative to current working directory
|
|
270
|
+
// For our purposes, we'll just prepend a fake absolute path
|
|
271
|
+
return ['/' + Clean(path), null]
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function Rel(
|
|
275
|
+
basepath: string,
|
|
276
|
+
targpath: string,
|
|
277
|
+
): [string, Error | null] {
|
|
278
|
+
// Simplified implementation - in reality this is much more complex
|
|
279
|
+
const base = Clean(basepath)
|
|
280
|
+
const targ = Clean(targpath)
|
|
281
|
+
|
|
282
|
+
if (base === targ) {
|
|
283
|
+
return ['.', null]
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Very basic relative path calculation
|
|
287
|
+
if (targ.startsWith(base + '/')) {
|
|
288
|
+
return [targ.substring(base.length + 1), null]
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return [targ, null]
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function EvalSymlinks(path: string): [string, Error | null] {
|
|
295
|
+
// No filesystem support, just return the cleaned path
|
|
296
|
+
return [Clean(path), null]
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function Glob(_pattern: string): [string[], Error | null] {
|
|
300
|
+
// No filesystem support, return empty array
|
|
301
|
+
return [[], null]
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// WalkFunc is the type of the function called for each file or directory
|
|
305
|
+
// visited by Walk. The path argument contains the argument to Walk as a
|
|
306
|
+
// prefix; that is, if Walk is called with "dir" and finds a file "a"
|
|
307
|
+
// in that directory, the walk function will be called with argument
|
|
308
|
+
// "dir/a". The info argument is the fs.FileInfo for the named path.
|
|
309
|
+
export type WalkFunc = (
|
|
310
|
+
path: string,
|
|
311
|
+
info: any,
|
|
312
|
+
err: Error | null,
|
|
313
|
+
) => Error | null
|
|
314
|
+
|
|
315
|
+
export function Walk(root: string, walkFn: WalkFunc): Error | null {
|
|
316
|
+
// No filesystem support, just call the function with the root
|
|
317
|
+
return walkFn(root, null, new Error('filesystem not supported'))
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function WalkDir(_root: string, _walkFn: any): Error | null {
|
|
321
|
+
// No filesystem support
|
|
322
|
+
return new Error('filesystem not supported')
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Localize is a stub - in Go it's used for Windows path localization
|
|
326
|
+
export function Localize(path: string): [string, Error | null] {
|
|
327
|
+
return [path, null]
|
|
328
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { DeepEqual } from './deepequal.js'
|
|
3
|
+
import { Value, BasicType, Int, String as StringType } from './type.js'
|
|
4
|
+
|
|
5
|
+
describe('DeepEqual', () => {
|
|
6
|
+
it('should compare primitive values correctly', () => {
|
|
7
|
+
expect(DeepEqual(5, 5)).toBe(true)
|
|
8
|
+
expect(DeepEqual(5, 10)).toBe(false)
|
|
9
|
+
|
|
10
|
+
expect(DeepEqual('hello', 'hello')).toBe(true)
|
|
11
|
+
expect(DeepEqual('hello', 'world')).toBe(false)
|
|
12
|
+
|
|
13
|
+
expect(DeepEqual(true, true)).toBe(true)
|
|
14
|
+
expect(DeepEqual(true, false)).toBe(false)
|
|
15
|
+
|
|
16
|
+
expect(DeepEqual(null, null)).toBe(true)
|
|
17
|
+
expect(DeepEqual(undefined, undefined)).toBe(true)
|
|
18
|
+
expect(DeepEqual(null, undefined)).toBe(false)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('should compare arrays correctly', () => {
|
|
22
|
+
expect(DeepEqual([1, 2, 3], [1, 2, 3])).toBe(true)
|
|
23
|
+
expect(DeepEqual([1, 2, 3], [1, 2, 4])).toBe(false)
|
|
24
|
+
expect(DeepEqual([1, 2, 3], [1, 2])).toBe(false)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('should compare objects correctly', () => {
|
|
28
|
+
expect(DeepEqual({ a: 1, b: 2 }, { a: 1, b: 2 })).toBe(true)
|
|
29
|
+
expect(DeepEqual({ a: 1, b: 2 }, { a: 1, b: 3 })).toBe(false)
|
|
30
|
+
expect(DeepEqual({ a: 1, b: 2 }, { a: 1 })).toBe(false)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should compare Value objects correctly', () => {
|
|
34
|
+
const v1 = new Value(42, new BasicType(Int, 'int'))
|
|
35
|
+
const v2 = new Value(42, new BasicType(Int, 'int'))
|
|
36
|
+
const v3 = new Value('hello', new BasicType(StringType, 'string'))
|
|
37
|
+
|
|
38
|
+
expect(DeepEqual(v1, v2)).toBe(true)
|
|
39
|
+
expect(DeepEqual(v1, v3)).toBe(false)
|
|
40
|
+
})
|
|
41
|
+
})
|
package/gs/reflect/deepequal.ts
CHANGED
|
@@ -49,7 +49,12 @@
|
|
|
49
49
|
// values that have been compared before, it treats the values as
|
|
50
50
|
// equal rather than examining the values to which they point.
|
|
51
51
|
// This ensures that DeepEqual terminates.
|
|
52
|
-
|
|
52
|
+
import { ReflectValue } from './types.js'
|
|
53
|
+
|
|
54
|
+
export function DeepEqual(
|
|
55
|
+
x: ReflectValue | null | undefined,
|
|
56
|
+
y: ReflectValue | null | undefined,
|
|
57
|
+
): boolean {
|
|
53
58
|
// Handle null/undefined cases
|
|
54
59
|
if (x === y) {
|
|
55
60
|
return true
|
|
@@ -112,7 +117,12 @@ export function DeepEqual(x: null | any, y: null | any): boolean {
|
|
|
112
117
|
|
|
113
118
|
// Compare elements
|
|
114
119
|
for (let i = 0; i < xMeta.length; i++) {
|
|
115
|
-
if (
|
|
120
|
+
if (
|
|
121
|
+
!DeepEqual(
|
|
122
|
+
xMeta.backing[i] as ReflectValue,
|
|
123
|
+
yMeta.backing[i] as ReflectValue,
|
|
124
|
+
)
|
|
125
|
+
) {
|
|
116
126
|
return false
|
|
117
127
|
}
|
|
118
128
|
}
|
|
@@ -127,7 +137,10 @@ export function DeepEqual(x: null | any, y: null | any): boolean {
|
|
|
127
137
|
return false
|
|
128
138
|
}
|
|
129
139
|
for (const [key, value] of x) {
|
|
130
|
-
if (
|
|
140
|
+
if (
|
|
141
|
+
!y.has(key) ||
|
|
142
|
+
!DeepEqual(value as ReflectValue, y.get(key) as ReflectValue)
|
|
143
|
+
) {
|
|
131
144
|
return false
|
|
132
145
|
}
|
|
133
146
|
}
|
|
@@ -142,7 +155,9 @@ export function DeepEqual(x: null | any, y: null | any): boolean {
|
|
|
142
155
|
return false
|
|
143
156
|
}
|
|
144
157
|
for (const key of keysX) {
|
|
145
|
-
|
|
158
|
+
const xObj = x as Record<string, ReflectValue>
|
|
159
|
+
const yObj = y as Record<string, ReflectValue>
|
|
160
|
+
if (!keysY.includes(key) || !DeepEqual(xObj[key], yObj[key])) {
|
|
146
161
|
return false
|
|
147
162
|
}
|
|
148
163
|
}
|