goscript 0.0.82 → 0.0.84
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/dist/gs/builtin/builtin.js +4 -7
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/hostio.d.ts +72 -0
- package/dist/gs/builtin/hostio.js +181 -0
- package/dist/gs/builtin/hostio.js.map +1 -0
- package/dist/gs/builtin/print.d.ts +8 -0
- package/dist/gs/builtin/print.js +111 -0
- package/dist/gs/builtin/print.js.map +1 -0
- package/dist/gs/fmt/fmt.js +2 -22
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/os/dir.gs.js +41 -27
- package/dist/gs/os/dir.gs.js.map +1 -1
- package/dist/gs/os/exec.gs.js +1 -2
- package/dist/gs/os/exec.gs.js.map +1 -1
- package/dist/gs/os/exec_posix.gs.js +1 -1
- package/dist/gs/os/exec_posix.gs.js.map +1 -1
- package/dist/gs/os/file_constants_js.gs.js +71 -7
- package/dist/gs/os/file_constants_js.gs.js.map +1 -1
- package/dist/gs/os/file_js.gs.js +168 -24
- package/dist/gs/os/file_js.gs.js.map +1 -1
- package/dist/gs/os/file_posix_js.gs.js +75 -11
- package/dist/gs/os/file_posix_js.gs.js.map +1 -1
- package/dist/gs/os/file_unix_js.gs.d.ts +3 -3
- package/dist/gs/os/file_unix_js.gs.js +218 -21
- package/dist/gs/os/file_unix_js.gs.js.map +1 -1
- package/dist/gs/os/getwd_js.gs.js +19 -6
- package/dist/gs/os/getwd_js.gs.js.map +1 -1
- package/dist/gs/os/path.gs.js +3 -3
- package/dist/gs/os/path.gs.js.map +1 -1
- package/dist/gs/os/pidfd_js.gs.js +0 -3
- package/dist/gs/os/pidfd_js.gs.js.map +1 -1
- package/dist/gs/os/proc.gs.js +6 -3
- package/dist/gs/os/proc.gs.js.map +1 -1
- package/dist/gs/os/proc_js.gs.js +10 -5
- package/dist/gs/os/proc_js.gs.js.map +1 -1
- package/dist/gs/os/rawconn_js.gs.d.ts +6 -3
- package/dist/gs/os/rawconn_js.gs.js +16 -10
- package/dist/gs/os/rawconn_js.gs.js.map +1 -1
- package/dist/gs/os/removeall_js.gs.js +2 -4
- package/dist/gs/os/removeall_js.gs.js.map +1 -1
- package/dist/gs/os/root_js.gs.d.ts +4 -1
- package/dist/gs/os/root_js.gs.js +90 -40
- package/dist/gs/os/root_js.gs.js.map +1 -1
- package/dist/gs/os/root_noopenat.gs.js +13 -16
- package/dist/gs/os/root_noopenat.gs.js.map +1 -1
- package/dist/gs/os/stat_js.gs.js +40 -4
- package/dist/gs/os/stat_js.gs.js.map +1 -1
- package/dist/gs/os/stat_unix_js.gs.d.ts +1 -1
- package/dist/gs/os/stat_unix_js.gs.js +5 -10
- package/dist/gs/os/stat_unix_js.gs.js.map +1 -1
- package/dist/gs/os/tempfile.gs.d.ts +1 -1
- package/dist/gs/os/tempfile.gs.js +24 -7
- package/dist/gs/os/tempfile.gs.js.map +1 -1
- package/dist/gs/os/types_js.gs.d.ts +30 -2
- package/dist/gs/os/types_js.gs.js +426 -23
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/gs/builtin/builtin.ts +4 -6
- package/gs/builtin/hostio.test.ts +69 -0
- package/gs/builtin/hostio.ts +298 -0
- package/gs/builtin/print.test.ts +48 -0
- package/gs/builtin/print.ts +154 -0
- package/gs/fmt/fmt.test.ts +41 -30
- package/gs/fmt/fmt.ts +2 -22
- package/gs/os/dir.gs.ts +40 -28
- package/gs/os/exec.gs.ts +2 -4
- package/gs/os/exec_posix.gs.ts +1 -2
- package/gs/os/file_constants_js.gs.ts +70 -8
- package/gs/os/file_js.gs.ts +156 -26
- package/gs/os/file_posix_js.gs.ts +69 -13
- package/gs/os/file_unix_js.gs.ts +212 -24
- package/gs/os/file_unix_js.test.ts +184 -0
- package/gs/os/getwd_js.gs.ts +18 -8
- package/gs/os/path.gs.ts +3 -4
- package/gs/os/pidfd_js.gs.ts +3 -8
- package/gs/os/proc.gs.ts +6 -4
- package/gs/os/proc_js.gs.ts +11 -8
- package/gs/os/rawconn_js.gs.ts +22 -14
- package/gs/os/removeall_js.gs.ts +3 -6
- package/gs/os/root_js.gs.ts +94 -42
- package/gs/os/root_noopenat.gs.ts +13 -20
- package/gs/os/stat_js.gs.ts +37 -6
- package/gs/os/stat_unix_js.gs.ts +6 -13
- package/gs/os/tempfile.gs.ts +27 -9
- package/gs/os/types_js.gs.ts +449 -26
- package/package.json +1 -1
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
export class HostUnsupportedError extends Error {
|
|
2
|
+
constructor() {
|
|
3
|
+
super('operation not implemented in JavaScript environment')
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type NodeFSModule = {
|
|
8
|
+
readSync(
|
|
9
|
+
fd: number,
|
|
10
|
+
buffer: Uint8Array,
|
|
11
|
+
offset?: number,
|
|
12
|
+
length?: number,
|
|
13
|
+
position?: number | null,
|
|
14
|
+
): number
|
|
15
|
+
writeSync(
|
|
16
|
+
fd: number,
|
|
17
|
+
buffer: Uint8Array,
|
|
18
|
+
offset?: number,
|
|
19
|
+
length?: number,
|
|
20
|
+
position?: number | null,
|
|
21
|
+
): number
|
|
22
|
+
closeSync?(fd: number): void
|
|
23
|
+
fstatSync?(fd: number): any
|
|
24
|
+
fsyncSync?(fd: number): void
|
|
25
|
+
ftruncateSync?(fd: number, len?: number): void
|
|
26
|
+
openSync?(path: string, flags: number | string, mode?: number): number
|
|
27
|
+
chmodSync?(path: string, mode: number): void
|
|
28
|
+
chownSync?(path: string, uid: number, gid: number): void
|
|
29
|
+
lchownSync?(path: string, uid: number, gid: number): void
|
|
30
|
+
linkSync?(existingPath: string, newPath: string): void
|
|
31
|
+
lstatSync?(path: string): any
|
|
32
|
+
mkdirSync?(path: string, options?: number | { mode?: number; recursive?: boolean }): void
|
|
33
|
+
readFileSync?(path: string): Uint8Array
|
|
34
|
+
readdirSync?(path: string, options?: { withFileTypes?: boolean }): any[]
|
|
35
|
+
readlinkSync?(path: string): string
|
|
36
|
+
renameSync?(oldPath: string, newPath: string): void
|
|
37
|
+
rmSync?(path: string, options?: { force?: boolean; recursive?: boolean }): void
|
|
38
|
+
rmdirSync?(path: string): void
|
|
39
|
+
statSync?(path: string): any
|
|
40
|
+
symlinkSync?(target: string, path: string): void
|
|
41
|
+
truncateSync?(path: string, len?: number): void
|
|
42
|
+
unlinkSync?(path: string): void
|
|
43
|
+
utimesSync?(path: string, atime: Date | number, mtime: Date | number): void
|
|
44
|
+
writeFileSync?(path: string, data: Uint8Array, options?: { mode?: number }): void
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type DenoStream = {
|
|
48
|
+
readSync?(buffer: Uint8Array): number | null
|
|
49
|
+
writeSync?(buffer: Uint8Array): number
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type DenoFileLike = DenoStream & {
|
|
53
|
+
close?(): void
|
|
54
|
+
rid?: number
|
|
55
|
+
seekSync?(offset: number, whence: number): number
|
|
56
|
+
syncSync?(): void
|
|
57
|
+
statSync?(): any
|
|
58
|
+
truncateSync?(len?: number): void
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type HostReadFD = (fd: number, buffer: Uint8Array) => number | null
|
|
62
|
+
type HostWriteFD = (fd: number, buffer: Uint8Array) => number
|
|
63
|
+
type HostTextWrite = (data: string) => void
|
|
64
|
+
|
|
65
|
+
export type HostRuntime = {
|
|
66
|
+
deno: any | null
|
|
67
|
+
nodeFS: NodeFSModule | null
|
|
68
|
+
platform: string
|
|
69
|
+
processObj: any | null
|
|
70
|
+
getEnv(name: string): string
|
|
71
|
+
getStdioHandle(fd: number): DenoFileLike | null
|
|
72
|
+
readFD: HostReadFD
|
|
73
|
+
writeFD: HostWriteFD
|
|
74
|
+
writeStderrText: HostTextWrite
|
|
75
|
+
writeStdoutText: HostTextWrite
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const encoder = new TextEncoder()
|
|
79
|
+
|
|
80
|
+
function getDynamicRequire(): ((specifier: string) => unknown) | null {
|
|
81
|
+
try {
|
|
82
|
+
return Function(
|
|
83
|
+
"return typeof require !== 'undefined' ? require : null",
|
|
84
|
+
)() as ((specifier: string) => unknown) | null
|
|
85
|
+
} catch {
|
|
86
|
+
return null
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function writeAllSync(
|
|
91
|
+
writeChunk: (chunk: Uint8Array) => number,
|
|
92
|
+
buffer: Uint8Array,
|
|
93
|
+
): number {
|
|
94
|
+
let offset = 0
|
|
95
|
+
while (offset < buffer.length) {
|
|
96
|
+
const n = writeChunk(buffer.subarray(offset))
|
|
97
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
98
|
+
throw new Error(`invalid write result: ${n}`)
|
|
99
|
+
}
|
|
100
|
+
if (n === 0) {
|
|
101
|
+
throw new Error('short write')
|
|
102
|
+
}
|
|
103
|
+
offset += n
|
|
104
|
+
}
|
|
105
|
+
return buffer.length
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function writeAllText(
|
|
109
|
+
writeChunk: (chunk: Uint8Array) => number,
|
|
110
|
+
data: string,
|
|
111
|
+
): void {
|
|
112
|
+
const bytes = encoder.encode(data)
|
|
113
|
+
if (bytes.length === 0) {
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
writeAllSync(writeChunk, bytes)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function detectNodeFS(processObj: any | null): NodeFSModule | null {
|
|
120
|
+
if (processObj && typeof processObj.getBuiltinModule === 'function') {
|
|
121
|
+
const module = processObj.getBuiltinModule('fs')
|
|
122
|
+
if (
|
|
123
|
+
module &&
|
|
124
|
+
typeof module.readSync === 'function' &&
|
|
125
|
+
typeof module.writeSync === 'function'
|
|
126
|
+
) {
|
|
127
|
+
return module as NodeFSModule
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const requireFn = getDynamicRequire()
|
|
132
|
+
if (requireFn) {
|
|
133
|
+
for (const specifier of ['node:fs', 'fs']) {
|
|
134
|
+
try {
|
|
135
|
+
const module = requireFn(specifier) as NodeFSModule | null
|
|
136
|
+
if (
|
|
137
|
+
module &&
|
|
138
|
+
typeof module.readSync === 'function' &&
|
|
139
|
+
typeof module.writeSync === 'function'
|
|
140
|
+
) {
|
|
141
|
+
return module
|
|
142
|
+
}
|
|
143
|
+
} catch {
|
|
144
|
+
// Try the next fallback.
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return null
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function unsupportedReadFD(_fd: number, _buffer: Uint8Array): number | null {
|
|
153
|
+
throw new HostUnsupportedError()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function unsupportedWriteFD(_fd: number, _buffer: Uint8Array): number {
|
|
157
|
+
throw new HostUnsupportedError()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function fallbackConsoleWriter(method: 'error' | 'log'): HostTextWrite {
|
|
161
|
+
return (data: string) => {
|
|
162
|
+
const consoleMethod = (globalThis as any).console?.[method]
|
|
163
|
+
if (!consoleMethod) {
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
if (data.endsWith('\n')) {
|
|
167
|
+
consoleMethod.call((globalThis as any).console, data.slice(0, -1))
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
consoleMethod.call((globalThis as any).console, data)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function detectHostRuntime(): HostRuntime {
|
|
175
|
+
const globalObj = globalThis as any
|
|
176
|
+
const deno = globalObj.Deno ?? null
|
|
177
|
+
const processObj = globalObj.process ?? null
|
|
178
|
+
const nodeFS = detectNodeFS(processObj)
|
|
179
|
+
|
|
180
|
+
const getStdioHandle = (fd: number): DenoFileLike | null => {
|
|
181
|
+
if (!deno) {
|
|
182
|
+
return null
|
|
183
|
+
}
|
|
184
|
+
switch (fd) {
|
|
185
|
+
case 0:
|
|
186
|
+
return deno.stdin ?? null
|
|
187
|
+
case 1:
|
|
188
|
+
return deno.stdout ?? null
|
|
189
|
+
case 2:
|
|
190
|
+
return deno.stderr ?? null
|
|
191
|
+
default:
|
|
192
|
+
return null
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const platform =
|
|
197
|
+
deno?.build?.os ??
|
|
198
|
+
processObj?.platform ??
|
|
199
|
+
'unknown'
|
|
200
|
+
|
|
201
|
+
const getEnv = (name: string): string => {
|
|
202
|
+
if (deno?.env?.get) {
|
|
203
|
+
try {
|
|
204
|
+
return deno.env.get(name) ?? ''
|
|
205
|
+
} catch {
|
|
206
|
+
return ''
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return processObj?.env?.[name] ?? ''
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let readFD: HostReadFD = unsupportedReadFD
|
|
213
|
+
let writeFD: HostWriteFD = unsupportedWriteFD
|
|
214
|
+
let writeStdoutText: HostTextWrite = fallbackConsoleWriter('log')
|
|
215
|
+
let writeStderrText: HostTextWrite = fallbackConsoleWriter('error')
|
|
216
|
+
|
|
217
|
+
if (deno) {
|
|
218
|
+
readFD = (fd: number, buffer: Uint8Array): number | null => {
|
|
219
|
+
const handle = getStdioHandle(fd)
|
|
220
|
+
if (!handle || typeof handle.readSync !== 'function') {
|
|
221
|
+
throw new HostUnsupportedError()
|
|
222
|
+
}
|
|
223
|
+
return handle.readSync(buffer)
|
|
224
|
+
}
|
|
225
|
+
writeFD = (fd: number, buffer: Uint8Array): number => {
|
|
226
|
+
const handle = getStdioHandle(fd)
|
|
227
|
+
if (!handle || typeof handle.writeSync !== 'function') {
|
|
228
|
+
throw new HostUnsupportedError()
|
|
229
|
+
}
|
|
230
|
+
return writeAllSync(
|
|
231
|
+
(chunk: Uint8Array) => handle.writeSync!(chunk),
|
|
232
|
+
buffer,
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
writeStdoutText = (data: string) => {
|
|
236
|
+
const handle = getStdioHandle(1)
|
|
237
|
+
if (!handle || typeof handle.writeSync !== 'function') {
|
|
238
|
+
fallbackConsoleWriter('log')(data)
|
|
239
|
+
return
|
|
240
|
+
}
|
|
241
|
+
writeAllText((chunk: Uint8Array) => handle.writeSync!(chunk), data)
|
|
242
|
+
}
|
|
243
|
+
writeStderrText = (data: string) => {
|
|
244
|
+
const handle = getStdioHandle(2)
|
|
245
|
+
if (!handle || typeof handle.writeSync !== 'function') {
|
|
246
|
+
fallbackConsoleWriter('error')(data)
|
|
247
|
+
return
|
|
248
|
+
}
|
|
249
|
+
writeAllText((chunk: Uint8Array) => handle.writeSync!(chunk), data)
|
|
250
|
+
}
|
|
251
|
+
} else if (nodeFS) {
|
|
252
|
+
readFD = (fd: number, buffer: Uint8Array): number | null =>
|
|
253
|
+
nodeFS.readSync(fd, buffer, 0, buffer.length, null)
|
|
254
|
+
writeFD = (fd: number, buffer: Uint8Array): number =>
|
|
255
|
+
writeAllSync(
|
|
256
|
+
(chunk: Uint8Array) =>
|
|
257
|
+
nodeFS.writeSync(fd, chunk, 0, chunk.length, null),
|
|
258
|
+
buffer,
|
|
259
|
+
)
|
|
260
|
+
writeStdoutText = (data: string) =>
|
|
261
|
+
writeAllText(
|
|
262
|
+
(chunk: Uint8Array) => nodeFS.writeSync(1, chunk, 0, chunk.length, null),
|
|
263
|
+
data,
|
|
264
|
+
)
|
|
265
|
+
writeStderrText = (data: string) =>
|
|
266
|
+
writeAllText(
|
|
267
|
+
(chunk: Uint8Array) => nodeFS.writeSync(2, chunk, 0, chunk.length, null),
|
|
268
|
+
data,
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
deno,
|
|
274
|
+
getEnv,
|
|
275
|
+
getStdioHandle,
|
|
276
|
+
nodeFS,
|
|
277
|
+
platform,
|
|
278
|
+
processObj,
|
|
279
|
+
readFD,
|
|
280
|
+
writeFD,
|
|
281
|
+
writeStderrText,
|
|
282
|
+
writeStdoutText,
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export let hostRuntime = detectHostRuntime()
|
|
287
|
+
|
|
288
|
+
export function resetHostRuntimeForTests(): void {
|
|
289
|
+
hostRuntime = detectHostRuntime()
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function writeHostStdoutText(data: string): void {
|
|
293
|
+
hostRuntime.writeStdoutText(data)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function writeHostStderrText(data: string): void {
|
|
297
|
+
hostRuntime.writeStderrText(data)
|
|
298
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { formatPrintedArgs } from './print.js'
|
|
4
|
+
|
|
5
|
+
describe('builtin println formatting', () => {
|
|
6
|
+
it('formats Uint8Array values with a stable inspect-style representation', () => {
|
|
7
|
+
expect(
|
|
8
|
+
formatPrintedArgs(['b2:', new Uint8Array([72, 101, 108, 108, 111])]),
|
|
9
|
+
).toBe('b2: Uint8Array(5) [ 72, 101, 108, 108, 111 ]')
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('quotes nested string array elements', () => {
|
|
13
|
+
expect(formatPrintedArgs(['strings.Split:', ['a', 'b', 'c']])).toBe(
|
|
14
|
+
'strings.Split: [ "a", "b", "c" ]',
|
|
15
|
+
)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('formats plain objects across multiple lines', () => {
|
|
19
|
+
expect(
|
|
20
|
+
formatPrintedArgs([
|
|
21
|
+
'out:',
|
|
22
|
+
{
|
|
23
|
+
exampleField: new Uint8Array([104, 101, 108, 108, 111]),
|
|
24
|
+
exampleText: 'world',
|
|
25
|
+
},
|
|
26
|
+
]),
|
|
27
|
+
).toBe(`out: {
|
|
28
|
+
exampleField: Uint8Array(5) [ 104, 101, 108, 108, 111 ],
|
|
29
|
+
exampleText: "world",
|
|
30
|
+
}`)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('formats goscript struct field bags using field values', () => {
|
|
34
|
+
expect(
|
|
35
|
+
formatPrintedArgs([
|
|
36
|
+
{
|
|
37
|
+
_fields: {
|
|
38
|
+
Name: { value: 'hello' },
|
|
39
|
+
Count: { value: 3 },
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
]),
|
|
43
|
+
).toBe(`{
|
|
44
|
+
Name: "hello",
|
|
45
|
+
Count: 3,
|
|
46
|
+
}`)
|
|
47
|
+
})
|
|
48
|
+
})
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { asArray, isSliceProxy, type Slice } from './slice.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* formatPrintedArgs formats builtin println arguments deterministically.
|
|
5
|
+
*/
|
|
6
|
+
export function formatPrintedArgs(args: readonly any[]): string {
|
|
7
|
+
return args.map((arg) => formatPrintedValue(arg)).join(' ')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* formatPrintedValue formats a single builtin println argument deterministically.
|
|
12
|
+
*/
|
|
13
|
+
export function formatPrintedValue(value: any): string {
|
|
14
|
+
return formatValue(value, 0, false, new WeakSet<object>())
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function formatValue(
|
|
18
|
+
value: any,
|
|
19
|
+
depth: number,
|
|
20
|
+
nested: boolean,
|
|
21
|
+
seen: WeakSet<object>,
|
|
22
|
+
): string {
|
|
23
|
+
if (value === null) {
|
|
24
|
+
return 'null'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (value === undefined) {
|
|
28
|
+
return '<nil>'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (typeof value === 'string') {
|
|
32
|
+
return nested ? JSON.stringify(value) : value
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (typeof value === 'boolean' || typeof value === 'number') {
|
|
36
|
+
return String(value)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (typeof value === 'bigint') {
|
|
40
|
+
return value.toString()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (value instanceof Uint8Array) {
|
|
44
|
+
return formatUint8Array(value)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
return formatArray(value, depth, seen)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (isSliceProxy(value as Slice<unknown>)) {
|
|
52
|
+
return formatArray(asArray(value as Slice<unknown>), depth, seen)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (typeof value === 'function') {
|
|
56
|
+
return value.name ? `[Function: ${value.name}]` : '[Function]'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (typeof value !== 'object') {
|
|
60
|
+
return String(value)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (seen.has(value)) {
|
|
64
|
+
return '[Circular]'
|
|
65
|
+
}
|
|
66
|
+
seen.add(value)
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
if (value instanceof Map) {
|
|
70
|
+
return formatArray(
|
|
71
|
+
Array.from(value.entries()).map(([k, v]) => `${formatValue(k, depth + 1, true, seen)} => ${formatValue(v, depth + 1, true, seen)}`),
|
|
72
|
+
depth,
|
|
73
|
+
seen,
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (value instanceof Set) {
|
|
78
|
+
return formatArray(Array.from(value.values()), depth, seen)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (value instanceof Error) {
|
|
82
|
+
return value.message || value.toString()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (typeof value.GoString === 'function') {
|
|
86
|
+
return value.GoString()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (typeof value.Error === 'function') {
|
|
90
|
+
return value.Error()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (typeof value.String === 'function') {
|
|
94
|
+
return value.String()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const entries = getObjectEntries(value)
|
|
98
|
+
if (entries.length === 0) {
|
|
99
|
+
return '{}'
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return formatObject(entries, depth, seen)
|
|
103
|
+
} finally {
|
|
104
|
+
seen.delete(value)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function formatUint8Array(value: Uint8Array): string {
|
|
109
|
+
if (value.length === 0) {
|
|
110
|
+
return 'Uint8Array(0) []'
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return `Uint8Array(${value.length}) [ ${Array.from(value).join(', ')} ]`
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function formatArray(value: readonly any[], depth: number, seen: WeakSet<object>): string {
|
|
117
|
+
if (value.length === 0) {
|
|
118
|
+
return '[]'
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return `[ ${value.map((item) => formatValue(item, depth + 1, true, seen)).join(', ')} ]`
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function formatObject(
|
|
125
|
+
entries: readonly [string, any][],
|
|
126
|
+
depth: number,
|
|
127
|
+
seen: WeakSet<object>,
|
|
128
|
+
): string {
|
|
129
|
+
const pad = ' '.repeat(depth + 1)
|
|
130
|
+
const closePad = ' '.repeat(depth)
|
|
131
|
+
const body = entries
|
|
132
|
+
.map(
|
|
133
|
+
([key, value]) =>
|
|
134
|
+
`${pad}${key}: ${formatValue(value, depth + 1, true, seen)},`,
|
|
135
|
+
)
|
|
136
|
+
.join('\n')
|
|
137
|
+
|
|
138
|
+
return `{\n${body}\n${closePad}}`
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function getObjectEntries(value: Record<string, any>): [string, any][] {
|
|
142
|
+
const fields = value._fields
|
|
143
|
+
if (fields && typeof fields === 'object' && !Array.isArray(fields)) {
|
|
144
|
+
return Object.keys(fields).map((key) => {
|
|
145
|
+
const field = fields[key]
|
|
146
|
+
if (field && typeof field === 'object' && 'value' in field) {
|
|
147
|
+
return [key, field.value]
|
|
148
|
+
}
|
|
149
|
+
return [key, field]
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return Object.entries(value).filter(([, entry]) => typeof entry !== 'function')
|
|
154
|
+
}
|
package/gs/fmt/fmt.test.ts
CHANGED
|
@@ -1,38 +1,49 @@
|
|
|
1
|
-
import { describe, it,
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { resetHostRuntimeForTests } from '@goscript/builtin/hostio.js'
|
|
2
3
|
import * as fmt from './fmt.js'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
const originalDeno = (globalThis as any).Deno
|
|
6
|
+
const originalProcess = (globalThis as any).process
|
|
7
|
+
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
if (originalDeno === undefined) {
|
|
10
|
+
delete (globalThis as any).Deno
|
|
11
|
+
} else {
|
|
12
|
+
;(globalThis as any).Deno = originalDeno
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (originalProcess === undefined) {
|
|
16
|
+
delete (globalThis as any).process
|
|
17
|
+
} else {
|
|
18
|
+
;(globalThis as any).process = originalProcess
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
resetHostRuntimeForTests()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// Helper to capture stdout via the hostio text output path.
|
|
6
25
|
function captureStdout(run: () => void): string {
|
|
7
26
|
let buf = ''
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// Fallback: spy on console.log for environments without process
|
|
26
|
-
const origLog = console.log
|
|
27
|
-
;(console as any).log = (msg: any) => {
|
|
28
|
-
buf += String(msg) + '\n'
|
|
29
|
-
}
|
|
30
|
-
try {
|
|
31
|
-
run()
|
|
32
|
-
} finally {
|
|
33
|
-
console.log = origLog
|
|
34
|
-
}
|
|
27
|
+
delete (globalThis as any).Deno
|
|
28
|
+
;(globalThis as any).process = {
|
|
29
|
+
getBuiltinModule: vi.fn(() => ({
|
|
30
|
+
readSync: vi.fn(),
|
|
31
|
+
writeSync: vi.fn(
|
|
32
|
+
(
|
|
33
|
+
_fd: number,
|
|
34
|
+
chunk: Uint8Array,
|
|
35
|
+
_offset?: number,
|
|
36
|
+
length?: number,
|
|
37
|
+
_position?: number | null,
|
|
38
|
+
) => {
|
|
39
|
+
buf += new TextDecoder().decode(chunk.subarray(0, length ?? chunk.length))
|
|
40
|
+
return length ?? chunk.length
|
|
41
|
+
},
|
|
42
|
+
),
|
|
43
|
+
})),
|
|
35
44
|
}
|
|
45
|
+
resetHostRuntimeForTests()
|
|
46
|
+
run()
|
|
36
47
|
|
|
37
48
|
return buf
|
|
38
49
|
}
|
package/gs/fmt/fmt.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import * as $ from '@goscript/builtin/index.js'
|
|
5
5
|
import * as errors from '@goscript/errors/index.js'
|
|
6
|
+
import { writeHostStdoutText } from '@goscript/builtin/hostio.js'
|
|
6
7
|
|
|
7
8
|
// Basic interfaces
|
|
8
9
|
export interface Stringer {
|
|
@@ -259,28 +260,7 @@ function parseFormat(format: string, args: any[]): string {
|
|
|
259
260
|
// Global stdout simulation for Print functions
|
|
260
261
|
let stdout = {
|
|
261
262
|
write: (data: string) => {
|
|
262
|
-
|
|
263
|
-
// but we need to avoid adding extra newlines that console.log adds
|
|
264
|
-
if (
|
|
265
|
-
typeof process !== 'undefined' &&
|
|
266
|
-
process.stdout &&
|
|
267
|
-
process.stdout.write
|
|
268
|
-
) {
|
|
269
|
-
process.stdout.write(data)
|
|
270
|
-
} else {
|
|
271
|
-
// In browser environments, we need to use console.log but handle newlines carefully
|
|
272
|
-
// If the data already ends with \n, we should strip it to avoid double newlines
|
|
273
|
-
if (data.endsWith('\n')) {
|
|
274
|
-
console.log(data.slice(0, -1))
|
|
275
|
-
} else {
|
|
276
|
-
// Use console.log without adding newline by using a custom method
|
|
277
|
-
if (console.log) {
|
|
278
|
-
// For data without newlines, we can just print it directly
|
|
279
|
-
// This is a bit of a hack but works for most cases
|
|
280
|
-
console.log(data)
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
263
|
+
writeHostStdoutText(data)
|
|
284
264
|
},
|
|
285
265
|
}
|
|
286
266
|
|
package/gs/os/dir.gs.ts
CHANGED
|
@@ -1,42 +1,54 @@
|
|
|
1
1
|
import * as $ from "@goscript/builtin/index.js";
|
|
2
2
|
import { ErrUnimplemented } from "./error.gs.js";
|
|
3
|
+
import { createFileInfo, getDeno, getNodeFS, newHostError } from "./types_js.gs.js";
|
|
4
|
+
import { FileInfoToDirEntry } from "@goscript/io/fs/readdir.js";
|
|
3
5
|
|
|
4
6
|
import * as fs from "@goscript/io/fs/index.js"
|
|
5
7
|
|
|
6
8
|
type DirEntry = fs.DirEntry;
|
|
7
9
|
|
|
8
|
-
// ReadDir reads the named directory,
|
|
9
|
-
// returning all its directory entries sorted by filename.
|
|
10
|
-
// If an error occurs reading the directory,
|
|
11
|
-
// ReadDir returns the entries it was able to read before the error,
|
|
12
|
-
// along with the error.
|
|
13
10
|
export function ReadDir(name: string): [$.Slice<DirEntry>, $.GoError] {
|
|
14
|
-
|
|
11
|
+
const denoObj = getDeno()
|
|
12
|
+
if (denoObj?.readDirSync) {
|
|
13
|
+
try {
|
|
14
|
+
const entries: DirEntry[] = []
|
|
15
|
+
for (const entry of denoObj.readDirSync(name)) {
|
|
16
|
+
const dirEntry = FileInfoToDirEntry(createFileInfo(entry.name, {
|
|
17
|
+
isDirectory: () => entry.isDirectory,
|
|
18
|
+
isSymbolicLink: () => entry.isSymlink,
|
|
19
|
+
mode: 0,
|
|
20
|
+
size: 0,
|
|
21
|
+
}))
|
|
22
|
+
if (dirEntry !== null) {
|
|
23
|
+
entries.push(dirEntry)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
entries.sort((a, b) => (a?.Name() ?? "").localeCompare(b?.Name() ?? ""))
|
|
27
|
+
return [$.arrayToSlice(entries), null]
|
|
28
|
+
} catch (err) {
|
|
29
|
+
return [null, newHostError(err)]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const nodeFS = getNodeFS()
|
|
33
|
+
if (nodeFS?.readdirSync) {
|
|
34
|
+
try {
|
|
35
|
+
const entries = nodeFS.readdirSync(name, { withFileTypes: true }).map((entry: any) =>
|
|
36
|
+
FileInfoToDirEntry(createFileInfo(entry.name, {
|
|
37
|
+
isDirectory: () => typeof entry.isDirectory === "function" ? entry.isDirectory() : false,
|
|
38
|
+
isSymbolicLink: () => typeof entry.isSymbolicLink === "function" ? entry.isSymbolicLink() : false,
|
|
39
|
+
mode: 0,
|
|
40
|
+
size: 0,
|
|
41
|
+
}))
|
|
42
|
+
).filter((entry: DirEntry | null): entry is DirEntry => entry !== null)
|
|
43
|
+
entries.sort((a, b) => (a?.Name() ?? "").localeCompare(b?.Name() ?? ""))
|
|
44
|
+
return [$.arrayToSlice(entries), null]
|
|
45
|
+
} catch (err) {
|
|
46
|
+
return [null, newHostError(err)]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
15
49
|
return [null, ErrUnimplemented]
|
|
16
50
|
}
|
|
17
51
|
|
|
18
|
-
// CopyFS copies the file system fsys into the directory dir,
|
|
19
|
-
// creating dir if necessary.
|
|
20
|
-
//
|
|
21
|
-
// Files are created with mode 0o666 plus any execute permissions
|
|
22
|
-
// from the source, and directories are created with mode 0o777
|
|
23
|
-
// (before umask).
|
|
24
|
-
//
|
|
25
|
-
// CopyFS will not overwrite existing files. If a file name in fsys
|
|
26
|
-
// already exists in the destination, CopyFS will return an error
|
|
27
|
-
// such that errors.Is(err, fs.ErrExist) will be true.
|
|
28
|
-
//
|
|
29
|
-
// Symbolic links in fsys are not supported. A *PathError with Err set
|
|
30
|
-
// to ErrInvalid is returned when copying from a symbolic link.
|
|
31
|
-
//
|
|
32
|
-
// Symbolic links in dir are followed.
|
|
33
|
-
//
|
|
34
|
-
// New files added to fsys (including if dir is a subdirectory of fsys)
|
|
35
|
-
// while CopyFS is running are not guaranteed to be copied.
|
|
36
|
-
//
|
|
37
|
-
// Copying stops at and returns the first error encountered.
|
|
38
52
|
export function CopyFS(dir: string, fsys: fs.FS): $.GoError {
|
|
39
|
-
// File system copying not supported in JavaScript environment
|
|
40
53
|
return ErrUnimplemented
|
|
41
54
|
}
|
|
42
|
-
|