goscript 0.2.4 → 0.2.5
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/README.md +8 -8
- package/cmd/go_js_wasm_exec/main.go +1 -1
- package/cmd/go_js_wasm_exec/main_test.go +1 -1
- package/cmd/goscript/cmd-compile.go +9 -1
- package/cmd/goscript/cmd-test.go +1 -1
- package/cmd/goscript/cmd_compile_test.go +44 -0
- package/cmd/goscript/deps.go +1 -1
- package/cmd/goscript-wasm/main.go +2 -2
- package/compiler/compile-request.go +19 -0
- package/compiler/compile_bench_test.go +121 -0
- package/compiler/compliance_test.go +17 -1
- package/compiler/config.go +2 -0
- package/compiler/gotest/result.go +1 -1
- package/compiler/gotest/runner.go +2 -2
- package/compiler/gotest/runner_test.go +4 -7
- package/compiler/index.test.ts +28 -0
- package/compiler/index.ts +32 -16
- package/compiler/lowering.go +1238 -194
- package/compiler/lowering_bench_test.go +4 -0
- package/compiler/override-facts.go +1 -1
- package/compiler/package-graph.go +92 -0
- package/compiler/package-graph_test.go +113 -0
- package/compiler/runtime-contract.go +1 -1
- package/compiler/semantic-model.go +32 -0
- package/compiler/skeleton_test.go +241 -15
- package/compiler/wasm/compile.go +1 -1
- package/compiler/wasm/compile_test.go +1 -1
- package/dist/compiler/index.d.ts +4 -0
- package/dist/compiler/index.js +26 -15
- package/dist/compiler/index.js.map +1 -1
- package/dist/gs/database/sql/driver/index.d.ts +165 -0
- package/dist/gs/database/sql/driver/index.js +432 -0
- package/dist/gs/database/sql/driver/index.js.map +1 -0
- package/dist/gs/encoding/binary/index.d.ts +71 -0
- package/dist/gs/encoding/binary/index.js +778 -0
- package/dist/gs/encoding/binary/index.js.map +1 -0
- package/dist/gs/fmt/fmt.js +156 -57
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/github.com/klauspost/cpuid/v2/index.d.ts +11 -0
- package/dist/gs/github.com/klauspost/cpuid/v2/index.js +28 -0
- package/dist/gs/github.com/klauspost/cpuid/v2/index.js.map +1 -0
- package/dist/gs/github.com/pkg/errors/errors.d.ts +0 -2
- package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
- package/dist/gs/github.com/pkg/errors/index.d.ts +2 -1
- package/dist/gs/github.com/pkg/errors/index.js +1 -1
- package/dist/gs/github.com/pkg/errors/index.js.map +1 -1
- package/dist/gs/github.com/pkg/errors/stack.d.ts +8 -19
- package/dist/gs/github.com/pkg/errors/stack.js +26 -61
- package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
- package/dist/gs/golang.org/x/crypto/cryptobyte/asn1/index.d.ts +19 -0
- package/dist/gs/golang.org/x/crypto/cryptobyte/asn1/index.js +25 -0
- package/dist/gs/golang.org/x/crypto/cryptobyte/asn1/index.js.map +1 -0
- package/dist/gs/golang.org/x/crypto/cryptobyte/index.d.ts +104 -0
- package/dist/gs/golang.org/x/crypto/cryptobyte/index.js +1107 -0
- package/dist/gs/golang.org/x/crypto/cryptobyte/index.js.map +1 -0
- package/dist/gs/golang.org/x/crypto/internal/alias/index.d.ts +3 -0
- package/dist/gs/golang.org/x/crypto/internal/alias/index.js +39 -0
- package/dist/gs/golang.org/x/crypto/internal/alias/index.js.map +1 -0
- package/dist/gs/runtime/runtime.d.ts +6 -1
- package/dist/gs/runtime/runtime.js +15 -8
- package/dist/gs/runtime/runtime.js.map +1 -1
- package/dist/gs/runtime/trace/index.d.ts +8 -5
- package/dist/gs/runtime/trace/index.js +324 -23
- package/dist/gs/runtime/trace/index.js.map +1 -1
- package/dist/gs/slices/slices.d.ts +2 -1
- package/dist/gs/slices/slices.js +9 -3
- package/dist/gs/slices/slices.js.map +1 -1
- package/dist/gs/sort/search.gs.d.ts +3 -1
- package/dist/gs/sort/search.gs.js +18 -53
- package/dist/gs/sort/search.gs.js.map +1 -1
- package/dist/gs/sync/sync.d.ts +1 -1
- package/dist/gs/sync/sync.js +3 -0
- package/dist/gs/sync/sync.js.map +1 -1
- package/dist/gs/time/time.d.ts +22 -29
- package/dist/gs/time/time.js +111 -32
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unsafe/unsafe.d.ts +3 -2
- package/dist/gs/unsafe/unsafe.js.map +1 -1
- package/go.mod +7 -5
- package/go.sum +12 -26
- package/gs/database/sql/driver/index.test.ts +88 -0
- package/gs/database/sql/driver/index.ts +675 -0
- package/gs/database/sql/driver/meta.json +3 -0
- package/gs/database/sql/driver/parity.json +144 -0
- package/gs/encoding/binary/index.test.ts +239 -0
- package/gs/encoding/binary/index.ts +999 -0
- package/gs/encoding/binary/meta.json +9 -0
- package/gs/encoding/binary/parity.json +72 -0
- package/gs/fmt/fmt.test.ts +28 -0
- package/gs/fmt/fmt.ts +198 -61
- package/gs/fmt/meta.json +2 -1
- package/gs/github.com/klauspost/cpuid/v2/index.ts +38 -0
- package/gs/github.com/klauspost/cpuid/v2/meta.json +3 -0
- package/gs/github.com/pkg/errors/errors.ts +1 -2
- package/gs/github.com/pkg/errors/index.ts +2 -1
- package/gs/github.com/pkg/errors/stack.ts +34 -62
- package/gs/golang.org/x/crypto/cryptobyte/asn1/index.test.ts +19 -0
- package/gs/golang.org/x/crypto/cryptobyte/asn1/index.ts +29 -0
- package/gs/golang.org/x/crypto/cryptobyte/index.test.ts +255 -0
- package/gs/golang.org/x/crypto/cryptobyte/index.ts +1441 -0
- package/gs/golang.org/x/crypto/cryptobyte/meta.json +3 -0
- package/gs/golang.org/x/crypto/internal/alias/index.test.ts +40 -0
- package/gs/golang.org/x/crypto/internal/alias/index.ts +40 -0
- package/gs/runtime/runtime.test.ts +16 -0
- package/gs/runtime/runtime.ts +17 -9
- package/gs/runtime/trace/index.test.ts +113 -14
- package/gs/runtime/trace/index.ts +384 -34
- package/gs/runtime/trace/meta.json +1 -0
- package/gs/slices/slices.test.ts +24 -1
- package/gs/slices/slices.ts +14 -4
- package/gs/sort/meta.json +1 -0
- package/gs/sort/search.gs.ts +20 -5
- package/gs/sync/sync.ts +4 -1
- package/gs/time/time.test.ts +79 -2
- package/gs/time/time.ts +133 -33
- package/gs/unsafe/unsafe.ts +4 -2
- package/package.json +2 -2
|
@@ -47,6 +47,8 @@ func BenchmarkLoweringPackage(b *testing.B) {
|
|
|
47
47
|
fixture.model,
|
|
48
48
|
fixture.semPkg,
|
|
49
49
|
make(map[string]map[types.Object]bool),
|
|
50
|
+
make(map[*types.Func]bool),
|
|
51
|
+
make(map[*types.Func]bool),
|
|
50
52
|
make(runtimeMethodSetCache),
|
|
51
53
|
LoweringOptions{},
|
|
52
54
|
); diagnosticsHaveErrors(diagnostics) {
|
|
@@ -86,6 +88,8 @@ func BenchmarkLoweringFile(b *testing.B) {
|
|
|
86
88
|
fixture.file.outputNames,
|
|
87
89
|
fixture.file.lazyPackageVars,
|
|
88
90
|
fixture.lazyPackageVarsByPkg,
|
|
91
|
+
make(map[*types.Func]bool),
|
|
92
|
+
make(map[*types.Func]bool),
|
|
89
93
|
make(runtimeMethodSetCache),
|
|
90
94
|
false,
|
|
91
95
|
"",
|
|
@@ -13,8 +13,8 @@ import (
|
|
|
13
13
|
"slices"
|
|
14
14
|
"strings"
|
|
15
15
|
|
|
16
|
-
gs "github.com/aperturerobotics/goscript"
|
|
17
16
|
jsoniter "github.com/aperturerobotics/json-iterator-lite"
|
|
17
|
+
gs "github.com/s4wave/goscript"
|
|
18
18
|
)
|
|
19
19
|
|
|
20
20
|
// OverrideFacts is the immutable compiler-visible view of GoScript overrides.
|
|
@@ -5,6 +5,7 @@ import (
|
|
|
5
5
|
"go/ast"
|
|
6
6
|
"os"
|
|
7
7
|
"slices"
|
|
8
|
+
"strconv"
|
|
8
9
|
"strings"
|
|
9
10
|
|
|
10
11
|
"golang.org/x/tools/go/packages"
|
|
@@ -163,6 +164,9 @@ func (o *PackageGraphOwner) Load(ctx context.Context, req *CompileRequest) (*Pac
|
|
|
163
164
|
Message: "package graph did not contain any package nodes",
|
|
164
165
|
})
|
|
165
166
|
}
|
|
167
|
+
if len(req.PackageBlocklist) != 0 {
|
|
168
|
+
diagnostics = append(diagnostics, packageBlocklistDiagnostics(graph, req.PackageBlocklist)...)
|
|
169
|
+
}
|
|
166
170
|
return graph, diagnostics
|
|
167
171
|
}
|
|
168
172
|
|
|
@@ -245,6 +249,94 @@ func newPackageGraphNode(pkg *packages.Package, requested bool, overrideFacts *O
|
|
|
245
249
|
}
|
|
246
250
|
}
|
|
247
251
|
|
|
252
|
+
func packageBlocklistDiagnostics(graph *PackageGraph, blocklist []string) []Diagnostic {
|
|
253
|
+
chain := packageBlocklistChain(graph, blocklist)
|
|
254
|
+
if len(chain) == 0 {
|
|
255
|
+
return nil
|
|
256
|
+
}
|
|
257
|
+
blocked := chain[len(chain)-1]
|
|
258
|
+
return []Diagnostic{{
|
|
259
|
+
Severity: DiagnosticSeverityError,
|
|
260
|
+
Code: "goscript/package-graph:blocklisted-package",
|
|
261
|
+
Message: "package graph contains blocklisted package " + strconv.Quote(blocked),
|
|
262
|
+
Detail: "import chain: " + strings.Join(chain, " -> "),
|
|
263
|
+
}}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
func packageBlocklistChain(graph *PackageGraph, blocklist []string) []string {
|
|
267
|
+
if graph == nil || len(graph.RequestedPackagePaths) == 0 {
|
|
268
|
+
return nil
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
roots := slices.Clone(graph.RequestedPackagePaths)
|
|
272
|
+
slices.Sort(roots)
|
|
273
|
+
|
|
274
|
+
type queueEntry struct {
|
|
275
|
+
path string
|
|
276
|
+
chain []string
|
|
277
|
+
}
|
|
278
|
+
queue := make([]queueEntry, 0, len(roots))
|
|
279
|
+
seen := make(map[string]bool)
|
|
280
|
+
for _, root := range roots {
|
|
281
|
+
if graph.NodesByPackagePath[root] == nil || seen[root] {
|
|
282
|
+
continue
|
|
283
|
+
}
|
|
284
|
+
seen[root] = true
|
|
285
|
+
queue = append(queue, queueEntry{
|
|
286
|
+
path: root,
|
|
287
|
+
chain: []string{root},
|
|
288
|
+
})
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
for len(queue) != 0 {
|
|
292
|
+
entry := queue[0]
|
|
293
|
+
queue = queue[1:]
|
|
294
|
+
if packagePathBlocklisted(entry.path, blocklist) {
|
|
295
|
+
return entry.chain
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
node := graph.NodesByPackagePath[entry.path]
|
|
299
|
+
if node == nil {
|
|
300
|
+
continue
|
|
301
|
+
}
|
|
302
|
+
// Override candidates are compiled from their GoScript override, whose
|
|
303
|
+
// TypeScript imports replace the native Go imports. collect prunes their
|
|
304
|
+
// native dependencies from the graph, so the blocklist walk must treat
|
|
305
|
+
// them as leaves; otherwise an overridden package (for example a
|
|
306
|
+
// reflect-free encoding/json override) would falsely chain to a
|
|
307
|
+
// blocklisted package it no longer imports in the compiled output.
|
|
308
|
+
if node.OverrideCandidate {
|
|
309
|
+
continue
|
|
310
|
+
}
|
|
311
|
+
imports := slices.Clone(node.Imports)
|
|
312
|
+
slices.Sort(imports)
|
|
313
|
+
for _, importPath := range imports {
|
|
314
|
+
if graph.NodesByPackagePath[importPath] == nil || seen[importPath] {
|
|
315
|
+
continue
|
|
316
|
+
}
|
|
317
|
+
seen[importPath] = true
|
|
318
|
+
nextChain := slices.Clone(entry.chain)
|
|
319
|
+
nextChain = append(nextChain, importPath)
|
|
320
|
+
queue = append(queue, queueEntry{
|
|
321
|
+
path: importPath,
|
|
322
|
+
chain: nextChain,
|
|
323
|
+
})
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return nil
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
func packagePathBlocklisted(path string, blocklist []string) bool {
|
|
330
|
+
for _, blocked := range blocklist {
|
|
331
|
+
// Match the package exactly or any subpackage, on a path-segment
|
|
332
|
+
// boundary so "crypto" blocks "crypto/ecdsa" but not "cryptobyte".
|
|
333
|
+
if path == blocked || strings.HasPrefix(path, blocked+"/") {
|
|
334
|
+
return true
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return false
|
|
338
|
+
}
|
|
339
|
+
|
|
248
340
|
func normalizePackageFileOrder(pkg *packages.Package) {
|
|
249
341
|
if pkg == nil {
|
|
250
342
|
return
|
|
@@ -266,6 +266,119 @@ func TestPackageGraphLoadsLocalReplacement(t *testing.T) {
|
|
|
266
266
|
}
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
+
func TestPackageBlocklistAllowsCleanFixture(t *testing.T) {
|
|
270
|
+
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
271
|
+
"go.mod": "module example.test/blockclean\n\ngo 1.25.3\n",
|
|
272
|
+
"main.go": "package blockclean\nimport \"example.test/blockclean/dep\"\nfunc Value() int { return dep.Value() }\n",
|
|
273
|
+
"dep/dep.go": "package dep\nfunc Value() int { return 1 }\n",
|
|
274
|
+
"other/doc.go": "package other\n",
|
|
275
|
+
})
|
|
276
|
+
comp, err := NewCompiler(&Config{
|
|
277
|
+
Dir: moduleDir,
|
|
278
|
+
OutputPath: filepath.Join(t.TempDir(), "out"),
|
|
279
|
+
AllDependencies: true,
|
|
280
|
+
PackageBlocklist: []string{"example.test/blockclean/other"},
|
|
281
|
+
}, nil, nil)
|
|
282
|
+
if err != nil {
|
|
283
|
+
t.Fatal(err.Error())
|
|
284
|
+
}
|
|
285
|
+
if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
|
|
286
|
+
t.Fatalf("compile with clean blocklist failed: %v", err)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
func TestPackageBlocklistReportsShortestImportChain(t *testing.T) {
|
|
291
|
+
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
292
|
+
"go.mod": "module example.test/blockchain\n\ngo 1.25.3\n",
|
|
293
|
+
"main.go": "package blockchain\nimport \"example.test/blockchain/dep\"\nfunc Value() int { return dep.Value() }\n",
|
|
294
|
+
"dep/dep.go": "package dep\nimport \"example.test/blockchain/mid/blocked\"\nfunc Value() int { return blocked.Value() }\n",
|
|
295
|
+
"mid/leaf.go": "package mid\nimport \"example.test/blockchain/mid/blocked\"\nfunc Leaf() int { return blocked.Value() }\n",
|
|
296
|
+
"mid/blocked/blocked.go": "package blocked\nfunc Value() int { return 1 }\n",
|
|
297
|
+
})
|
|
298
|
+
comp, err := NewCompiler(&Config{
|
|
299
|
+
Dir: moduleDir,
|
|
300
|
+
OutputPath: filepath.Join(t.TempDir(), "out"),
|
|
301
|
+
AllDependencies: true,
|
|
302
|
+
PackageBlocklist: []string{"example.test/blockchain/mid/blocked"},
|
|
303
|
+
}, nil, nil)
|
|
304
|
+
if err != nil {
|
|
305
|
+
t.Fatal(err.Error())
|
|
306
|
+
}
|
|
307
|
+
_, err = comp.CompilePackages(context.Background(), ".")
|
|
308
|
+
if err == nil {
|
|
309
|
+
t.Fatal("expected blocklisted package diagnostic")
|
|
310
|
+
}
|
|
311
|
+
text := err.Error()
|
|
312
|
+
if !strings.Contains(text, "goscript/package-graph:blocklisted-package") {
|
|
313
|
+
t.Fatalf("expected blocklist diagnostic, got %q", text)
|
|
314
|
+
}
|
|
315
|
+
if !strings.Contains(text, `package graph contains blocklisted package "example.test/blockchain/mid/blocked"`) {
|
|
316
|
+
t.Fatalf("expected blocklisted package name, got %q", text)
|
|
317
|
+
}
|
|
318
|
+
expected := "example.test/blockchain -> example.test/blockchain/dep -> example.test/blockchain/mid/blocked"
|
|
319
|
+
if !strings.Contains(text, expected) {
|
|
320
|
+
t.Fatalf("expected shortest import chain %q, got %q", expected, text)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
func TestPackageBlocklistIgnoresOverrideCandidateImports(t *testing.T) {
|
|
325
|
+
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
326
|
+
"go.mod": strings.Join([]string{
|
|
327
|
+
"module example.test/blockoverride",
|
|
328
|
+
"",
|
|
329
|
+
"go 1.25.3",
|
|
330
|
+
"",
|
|
331
|
+
"require example.test/over v0.0.0",
|
|
332
|
+
"replace example.test/over => ./over",
|
|
333
|
+
"",
|
|
334
|
+
}, "\n"),
|
|
335
|
+
"main.go": "package blockoverride\nimport (\n\t\"example.test/over\"\n\t\"example.test/blockoverride/dep\"\n)\nfunc Value() int { return over.Value() + dep.Value() }\n",
|
|
336
|
+
"dep/dep.go": "package dep\nimport \"example.test/blockoverride/mid\"\nfunc Value() int { return mid.Value() }\n",
|
|
337
|
+
"mid/mid.go": "package mid\nimport \"example.test/blockoverride/mid/blocked\"\nfunc Value() int { return blocked.Value() }\n",
|
|
338
|
+
"mid/blocked/blocked.go": "package blocked\nfunc Value() int { return 1 }\n",
|
|
339
|
+
"over/go.mod": strings.Join([]string{
|
|
340
|
+
"module example.test/over",
|
|
341
|
+
"",
|
|
342
|
+
"go 1.25.3",
|
|
343
|
+
"",
|
|
344
|
+
"require example.test/blockoverride v0.0.0",
|
|
345
|
+
"replace example.test/blockoverride => ../",
|
|
346
|
+
"",
|
|
347
|
+
}, "\n"),
|
|
348
|
+
"over/over.go": "package over\nimport \"example.test/blockoverride/mid/blocked\"\nfunc Value() int { return blocked.Value() }\n",
|
|
349
|
+
})
|
|
350
|
+
overrideDir := filepath.Join(t.TempDir(), "gs")
|
|
351
|
+
writeFixtureFile(t, overrideDir, "example.test/over/index.ts", "export function Value(): number { return 0 }\n")
|
|
352
|
+
|
|
353
|
+
overrideOwner := NewOverrideRegistryOwner(overrideDir)
|
|
354
|
+
req := &CompileRequest{
|
|
355
|
+
Patterns: []string{"."},
|
|
356
|
+
Dir: moduleDir,
|
|
357
|
+
OutputPath: filepath.Join(t.TempDir(), "out"),
|
|
358
|
+
DependencyMode: DependencyModeAll,
|
|
359
|
+
RuntimeEmissionMode: RuntimeEmissionModeEmit,
|
|
360
|
+
PackageBlocklist: []string{"example.test/blockoverride/mid/blocked"},
|
|
361
|
+
}
|
|
362
|
+
graph, diagnostics := NewPackageGraphOwner(overrideOwner).Load(context.Background(), req)
|
|
363
|
+
if diagnosticsHaveErrors(diagnostics) {
|
|
364
|
+
chain := packageBlocklistChain(graph, req.PackageBlocklist)
|
|
365
|
+
if slices.Contains(chain, "example.test/over") {
|
|
366
|
+
t.Fatalf("blocklist chain routed through override candidate: %v", chain)
|
|
367
|
+
}
|
|
368
|
+
expected := []string{
|
|
369
|
+
"example.test/blockoverride",
|
|
370
|
+
"example.test/blockoverride/dep",
|
|
371
|
+
"example.test/blockoverride/mid",
|
|
372
|
+
"example.test/blockoverride/mid/blocked",
|
|
373
|
+
}
|
|
374
|
+
if !slices.Equal(chain, expected) {
|
|
375
|
+
t.Fatalf("expected real import chain %v, got %v", expected, chain)
|
|
376
|
+
}
|
|
377
|
+
} else {
|
|
378
|
+
t.Fatal("expected blocklisted package diagnostic via the real import path")
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
269
382
|
func TestPackageGraphDetectsOverrideCandidates(t *testing.T) {
|
|
270
383
|
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
271
384
|
"go.mod": "module example.test/override\n\ngo 1.25.3\n",
|
|
@@ -756,6 +756,15 @@ func (o *SemanticModelOwner) collectFunctionFacts(
|
|
|
756
756
|
if typed.Op == token.ARROW {
|
|
757
757
|
markFunctionAsync(semFn, "channel-receive")
|
|
758
758
|
}
|
|
759
|
+
case *ast.RangeStmt:
|
|
760
|
+
if signatureForType(pkg.TypesInfo.TypeOf(typed.X)) != nil {
|
|
761
|
+
if called := calledFunction(pkg, typed.X); called != nil {
|
|
762
|
+
semFn.calls[functionOriginOrSelf(called)] = true
|
|
763
|
+
}
|
|
764
|
+
if rangeFunctionExprNeedsAwait(model, pkg, overrideFacts, typed.X) {
|
|
765
|
+
markFunctionAsync(semFn, "range-function")
|
|
766
|
+
}
|
|
767
|
+
}
|
|
759
768
|
case *ast.CallExpr:
|
|
760
769
|
if called := calledFunction(pkg, typed.Fun); called != nil {
|
|
761
770
|
semFn.calls[functionOriginOrSelf(called)] = true
|
|
@@ -785,6 +794,29 @@ func (o *SemanticModelOwner) collectFunctionFacts(
|
|
|
785
794
|
return diagnostics
|
|
786
795
|
}
|
|
787
796
|
|
|
797
|
+
func rangeFunctionExprNeedsAwait(
|
|
798
|
+
model *SemanticModel,
|
|
799
|
+
pkg *packages.Package,
|
|
800
|
+
overrideFacts *OverrideFacts,
|
|
801
|
+
expr ast.Expr,
|
|
802
|
+
) bool {
|
|
803
|
+
if model == nil || pkg == nil || signatureForType(pkg.TypesInfo.TypeOf(expr)) == nil {
|
|
804
|
+
return false
|
|
805
|
+
}
|
|
806
|
+
if called := calledFunction(pkg, expr); called != nil {
|
|
807
|
+
if semFn := semanticFunctionFor(model, called); semFn != nil && semFn.async {
|
|
808
|
+
return true
|
|
809
|
+
}
|
|
810
|
+
if called.Pkg() != nil && overrideFacts.IsFunctionAsync(called.Pkg().Path(), called.Name()) {
|
|
811
|
+
return true
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
if overrideFacts.IsMethodAsync(overrideCallPackage(pkg, expr), overrideCallMethod(pkg, expr)) {
|
|
815
|
+
return true
|
|
816
|
+
}
|
|
817
|
+
return callUsesFunctionValue(pkg, expr)
|
|
818
|
+
}
|
|
819
|
+
|
|
788
820
|
func recordImmediateFuncLitAsyncFacts(
|
|
789
821
|
model *SemanticModel,
|
|
790
822
|
pkg *packages.Package,
|
|
@@ -789,6 +789,61 @@ func TestCompilePackagesReadsShadowedVarRefStructFieldsOnce(t *testing.T) {
|
|
|
789
789
|
}
|
|
790
790
|
}
|
|
791
791
|
|
|
792
|
+
func TestCompilePackagesUnwrapsAnonymousStructPointerFieldReceivers(t *testing.T) {
|
|
793
|
+
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
794
|
+
"go.mod": "module example.test/anonstructptr\n\ngo 1.25.3\n",
|
|
795
|
+
"main.go": strings.Join([]string{
|
|
796
|
+
"package anonstructptr",
|
|
797
|
+
"func mergeCore(dst, src *struct {",
|
|
798
|
+
" IsBare bool",
|
|
799
|
+
" Worktree string",
|
|
800
|
+
"}) {",
|
|
801
|
+
" if src.IsBare {",
|
|
802
|
+
" dst.IsBare = true",
|
|
803
|
+
" }",
|
|
804
|
+
" if src.Worktree != \"\" {",
|
|
805
|
+
" dst.Worktree = src.Worktree",
|
|
806
|
+
" }",
|
|
807
|
+
"}",
|
|
808
|
+
"",
|
|
809
|
+
}, "\n"),
|
|
810
|
+
})
|
|
811
|
+
outputDir := filepath.Join(t.TempDir(), "output")
|
|
812
|
+
comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
|
|
813
|
+
if err != nil {
|
|
814
|
+
t.Fatal(err.Error())
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
|
|
818
|
+
t.Fatal(err.Error())
|
|
819
|
+
}
|
|
820
|
+
outputFile := filepath.Join(outputDir, "@goscript", "example.test", "anonstructptr", "main.gs.ts")
|
|
821
|
+
content, err := os.ReadFile(outputFile)
|
|
822
|
+
if err != nil {
|
|
823
|
+
t.Fatal(err.Error())
|
|
824
|
+
}
|
|
825
|
+
text := string(content)
|
|
826
|
+
for _, want := range []string{
|
|
827
|
+
`$.pointerValue<{"IsBare": boolean, "Worktree": string}>(src).IsBare`,
|
|
828
|
+
`$.pointerValue<{"IsBare": boolean, "Worktree": string}>(dst).IsBare = true`,
|
|
829
|
+
`$.pointerValue<{"IsBare": boolean, "Worktree": string}>(dst).Worktree = $.pointerValue<{"IsBare": boolean, "Worktree": string}>(src).Worktree`,
|
|
830
|
+
} {
|
|
831
|
+
if !strings.Contains(text, want) {
|
|
832
|
+
t.Fatalf("missing %q in generated output:\n%s", want, text)
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
for _, bad := range []string{
|
|
836
|
+
"src.IsBare",
|
|
837
|
+
"dst.IsBare",
|
|
838
|
+
"src.Worktree",
|
|
839
|
+
"dst.Worktree",
|
|
840
|
+
} {
|
|
841
|
+
if strings.Contains(text, bad) {
|
|
842
|
+
t.Fatalf("anonymous struct pointer field receiver stayed wrapped at %q:\n%s", bad, text)
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
792
847
|
func TestCompilePackagesWrapsChannelSendInterfaceValues(t *testing.T) {
|
|
793
848
|
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
794
849
|
"go.mod": "module example.test/chansendiface\n\ngo 1.25.3\n",
|
|
@@ -1436,7 +1491,7 @@ func TestCompilePackagesEmitsSideEffectImportsForInterfaceRegistry(t *testing.T)
|
|
|
1436
1491
|
"import * as dep from \"@goscript/example.test/interface-registry/dep/index.js\"",
|
|
1437
1492
|
"import \"@goscript/example.test/interface-registry/dep/index.js\"",
|
|
1438
1493
|
"import type * as __goscript_local from \"./local.gs.ts\"",
|
|
1439
|
-
"case $.typeAssert<
|
|
1494
|
+
"case $.typeAssert<__goscript_local.Local | null>(__goscriptTypeSwitchValue, \"main.Local\").ok",
|
|
1440
1495
|
"$.typeAssertTuple<dep.Remote | null>(v, \"dep.Remote\")",
|
|
1441
1496
|
} {
|
|
1442
1497
|
if !strings.Contains(mainText, want) {
|
|
@@ -1546,6 +1601,56 @@ func TestCompilePackagesLowersUnsafeBytePointerArithmetic(t *testing.T) {
|
|
|
1546
1601
|
}
|
|
1547
1602
|
}
|
|
1548
1603
|
|
|
1604
|
+
func TestCompilePackagesLowersGMSUnsafeArrayPointerConversions(t *testing.T) {
|
|
1605
|
+
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
1606
|
+
"go.mod": "module example.test/gmsunsafe\n\ngo 1.25.3\n",
|
|
1607
|
+
"main.go": strings.Join([]string{
|
|
1608
|
+
"package main",
|
|
1609
|
+
"import (",
|
|
1610
|
+
" \"reflect\"",
|
|
1611
|
+
" \"unsafe\"",
|
|
1612
|
+
")",
|
|
1613
|
+
"func IntBytes(n int64) byte {",
|
|
1614
|
+
" mem := (*[8]byte)(unsafe.Pointer(&n))",
|
|
1615
|
+
" bytes := *mem",
|
|
1616
|
+
" return bytes[0]",
|
|
1617
|
+
"}",
|
|
1618
|
+
"func StringToBytes(str string) []byte {",
|
|
1619
|
+
" if len(str) == 0 {",
|
|
1620
|
+
" return []byte{}",
|
|
1621
|
+
" }",
|
|
1622
|
+
" return (*[0x7fff0000]byte)(unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&str)).Data))[:len(str):len(str)]",
|
|
1623
|
+
"}",
|
|
1624
|
+
"",
|
|
1625
|
+
}, "\n"),
|
|
1626
|
+
})
|
|
1627
|
+
outputDir := filepath.Join(t.TempDir(), "output")
|
|
1628
|
+
comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
|
|
1629
|
+
if err != nil {
|
|
1630
|
+
t.Fatal(err.Error())
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
_, err = comp.CompilePackages(context.Background(), ".")
|
|
1634
|
+
if err != nil {
|
|
1635
|
+
t.Fatal(err.Error())
|
|
1636
|
+
}
|
|
1637
|
+
outputFile := filepath.Join(outputDir, "@goscript", "example.test", "gmsunsafe", "main.gs.ts")
|
|
1638
|
+
content, err := os.ReadFile(outputFile)
|
|
1639
|
+
if err != nil {
|
|
1640
|
+
t.Fatal(err.Error())
|
|
1641
|
+
}
|
|
1642
|
+
text := string(content)
|
|
1643
|
+
if !strings.Contains(text, "$.arrayPointerFromIndexRef<number>($.indexRef([n.value], 0), 8, 8, 1)") {
|
|
1644
|
+
t.Fatalf("missing scalar unsafe array pointer conversion:\n%s", text)
|
|
1645
|
+
}
|
|
1646
|
+
if !strings.Contains(text, "$.arrayPointerFromIndexRef<number>($.indexRef($.stringToBytes(str.value), 0), 2147418112, 1, 1)") {
|
|
1647
|
+
t.Fatalf("missing string data unsafe array pointer conversion:\n%s", text)
|
|
1648
|
+
}
|
|
1649
|
+
if strings.Contains(text, "unsafe.Pointer(&n) as any") || strings.Contains(text, "StringHeader)(unsafe.Pointer(&str)).Data) as any") {
|
|
1650
|
+
t.Fatalf("unsafe array pointer conversion fell back to any cast:\n%s", text)
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1549
1654
|
func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
|
|
1550
1655
|
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
1551
1656
|
"go.mod": "module example.test/structs\n\ngo 1.25.3\n",
|
|
@@ -1669,6 +1774,124 @@ func TestCompilePackagesEscapesReservedTypeNames(t *testing.T) {
|
|
|
1669
1774
|
}
|
|
1670
1775
|
}
|
|
1671
1776
|
|
|
1777
|
+
func TestCompilePackagesEscapesStrictModeRestrictedIdentifiers(t *testing.T) {
|
|
1778
|
+
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
1779
|
+
"go.mod": "module example.test/strictidents\n\ngo 1.25.3\n",
|
|
1780
|
+
"main.go": strings.Join([]string{
|
|
1781
|
+
"package main",
|
|
1782
|
+
"func choose(arguments int) (eval int) {",
|
|
1783
|
+
" eval = arguments + 1",
|
|
1784
|
+
" arguments = eval + arguments",
|
|
1785
|
+
" return",
|
|
1786
|
+
"}",
|
|
1787
|
+
"func main() {",
|
|
1788
|
+
" eval := choose(1)",
|
|
1789
|
+
" arguments := eval + 1",
|
|
1790
|
+
" println(eval, arguments)",
|
|
1791
|
+
"}",
|
|
1792
|
+
"",
|
|
1793
|
+
}, "\n"),
|
|
1794
|
+
})
|
|
1795
|
+
outputDir := filepath.Join(t.TempDir(), "output")
|
|
1796
|
+
comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
|
|
1797
|
+
if err != nil {
|
|
1798
|
+
t.Fatal(err.Error())
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
|
|
1802
|
+
t.Fatal(err.Error())
|
|
1803
|
+
}
|
|
1804
|
+
outputFile := filepath.Join(outputDir, "@goscript", "example.test", "strictidents", "main.gs.ts")
|
|
1805
|
+
content, err := os.ReadFile(outputFile)
|
|
1806
|
+
if err != nil {
|
|
1807
|
+
t.Fatal(err.Error())
|
|
1808
|
+
}
|
|
1809
|
+
text := string(content)
|
|
1810
|
+
for _, want := range []string{
|
|
1811
|
+
"function choose(_arguments: number): number",
|
|
1812
|
+
"let _eval: number = 0",
|
|
1813
|
+
"_eval = _arguments + 1",
|
|
1814
|
+
"_arguments = _eval + _arguments",
|
|
1815
|
+
"let _eval = choose(1)",
|
|
1816
|
+
"let _arguments = _eval + 1",
|
|
1817
|
+
} {
|
|
1818
|
+
if !strings.Contains(text, want) {
|
|
1819
|
+
t.Fatalf("missing %q in generated output:\n%s", want, text)
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
for _, bad := range []string{
|
|
1823
|
+
"let eval",
|
|
1824
|
+
" eval =",
|
|
1825
|
+
"function choose(arguments",
|
|
1826
|
+
"let arguments",
|
|
1827
|
+
" arguments =",
|
|
1828
|
+
} {
|
|
1829
|
+
if strings.Contains(text, bad) {
|
|
1830
|
+
t.Fatalf("strict-mode restricted identifier was not escaped, found %q:\n%s", bad, text)
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
func TestCompilePackagesDoesNotEmitHiddenEmbeddedMethodOverField(t *testing.T) {
|
|
1836
|
+
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
1837
|
+
"go.mod": "module example.test/hiddenembeddedmethod\n\ngo 1.25.3\n",
|
|
1838
|
+
"main.go": strings.Join([]string{
|
|
1839
|
+
"package main",
|
|
1840
|
+
"type embedded struct{}",
|
|
1841
|
+
"func (embedded) Database() string {",
|
|
1842
|
+
" return \"method\"",
|
|
1843
|
+
"}",
|
|
1844
|
+
"type holder struct {",
|
|
1845
|
+
" Database string",
|
|
1846
|
+
" embedded",
|
|
1847
|
+
"}",
|
|
1848
|
+
"func value(h holder) string {",
|
|
1849
|
+
" return h.Database",
|
|
1850
|
+
"}",
|
|
1851
|
+
"",
|
|
1852
|
+
}, "\n"),
|
|
1853
|
+
})
|
|
1854
|
+
outputDir := filepath.Join(t.TempDir(), "output")
|
|
1855
|
+
comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
|
|
1856
|
+
if err != nil {
|
|
1857
|
+
t.Fatal(err.Error())
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
|
|
1861
|
+
t.Fatal(err.Error())
|
|
1862
|
+
}
|
|
1863
|
+
outputFile := filepath.Join(outputDir, "@goscript", "example.test", "hiddenembeddedmethod", "main.gs.ts")
|
|
1864
|
+
content, err := os.ReadFile(outputFile)
|
|
1865
|
+
if err != nil {
|
|
1866
|
+
t.Fatal(err.Error())
|
|
1867
|
+
}
|
|
1868
|
+
text := string(content)
|
|
1869
|
+
for _, want := range []string{
|
|
1870
|
+
"public get Database(): string",
|
|
1871
|
+
"public set Database(value: string)",
|
|
1872
|
+
"return h.Database",
|
|
1873
|
+
} {
|
|
1874
|
+
if !strings.Contains(text, want) {
|
|
1875
|
+
t.Fatalf("missing %q in generated output:\n%s", want, text)
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
if got := strings.Count(text, "public Database("); got != 1 {
|
|
1879
|
+
t.Fatalf("expected only embedded.Database method, got %d declarations:\n%s", got, text)
|
|
1880
|
+
}
|
|
1881
|
+
holderStart := strings.Index(text, "export class holder")
|
|
1882
|
+
embeddedStart := strings.Index(text, "export class embedded")
|
|
1883
|
+
if holderStart < 0 || embeddedStart < 0 {
|
|
1884
|
+
t.Fatalf("missing holder or embedded class:\n%s", text)
|
|
1885
|
+
}
|
|
1886
|
+
holderText := text[holderStart:]
|
|
1887
|
+
if embeddedStart > holderStart {
|
|
1888
|
+
holderText = text[holderStart:embeddedStart]
|
|
1889
|
+
}
|
|
1890
|
+
if strings.Contains(holderText, "public Database(") {
|
|
1891
|
+
t.Fatalf("hidden embedded method was emitted on holder:\n%s", holderText)
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1672
1895
|
func TestCompilePackagesAvoidsPointerMethodTypeNameShadow(t *testing.T) {
|
|
1673
1896
|
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
1674
1897
|
"go.mod": "module example.test/methodshadow\n\ngo 1.25.3\n",
|
|
@@ -1907,9 +2130,9 @@ func TestCompilePackagesEmitsArraySliceMapStringAndNamedMethods(t *testing.T) {
|
|
|
1907
2130
|
"let literal: $.Slice<number> = $.arrayToSlice<number>([1, 2])",
|
|
1908
2131
|
"literal = $.append(literal, 3)",
|
|
1909
2132
|
"slice![0] = arr[1]",
|
|
1910
|
-
"let m: Map<string, number> | null = $.makeMap<string, number>()",
|
|
2133
|
+
"let m: globalThis.Map<string, number> | null = $.makeMap<string, number>()",
|
|
1911
2134
|
"$.mapSet(m, \"one\", 1)",
|
|
1912
|
-
"let [value, ok] = $.mapGet(m, \"missing\", 0)",
|
|
2135
|
+
"let [value, ok] = $.mapGet<string, number, number>(m, \"missing\", 0)",
|
|
1913
2136
|
"slice![0]",
|
|
1914
2137
|
"literal![2]",
|
|
1915
2138
|
"let list: $.VarRef<MySlice> = $.varRef(null as MySlice)",
|
|
@@ -2447,8 +2670,8 @@ func TestCompilePackagesEmitsInterfacesMethodValuesTypeSwitchesAndFunctionAssert
|
|
|
2447
2670
|
"elemType: { kind: $.TypeKind.Struct, methods: [], fields: [{ name: \"Name\", key: \"Name\", type: { kind: $.TypeKind.Basic, name: \"string\" }",
|
|
2448
2671
|
"let fn = __goscriptTuple",
|
|
2449
2672
|
"switch (true)",
|
|
2450
|
-
"case $.typeAssert<
|
|
2451
|
-
"let v:
|
|
2673
|
+
"case $.typeAssert<ReadCloser | null>(__goscriptTypeSwitchValue, \"main.ReadCloser\").ok",
|
|
2674
|
+
"let v: ReadCloser | null = $.typeAssert<ReadCloser | null>(__goscriptTypeSwitchValue, \"main.ReadCloser\").value",
|
|
2452
2675
|
} {
|
|
2453
2676
|
if !strings.Contains(text, want) {
|
|
2454
2677
|
t.Fatalf("missing %q in generated output:\n%s", want, text)
|
|
@@ -2541,8 +2764,8 @@ func TestCompilePackagesUsesNonNilInterfaceTypeSwitchCaseVarRefs(t *testing.T) {
|
|
|
2541
2764
|
text := string(content)
|
|
2542
2765
|
for _, want := range []string{
|
|
2543
2766
|
"export type tcpConn = {",
|
|
2544
|
-
"case $.typeAssert<
|
|
2545
|
-
"let c: $.VarRef<
|
|
2767
|
+
"case $.typeAssert<tcpConn | null>(__goscriptTypeSwitchValue, \"main.tcpConn\").ok",
|
|
2768
|
+
"let c: $.VarRef<tcpConn | null> = $.varRef($.typeAssert<tcpConn | null>(__goscriptTypeSwitchValue, \"main.tcpConn\").value)",
|
|
2546
2769
|
"$.pointerValue<Exclude<tcpConn, null>>(c.value).SyscallConn()",
|
|
2547
2770
|
} {
|
|
2548
2771
|
if !strings.Contains(text, want) {
|
|
@@ -3454,7 +3677,7 @@ func TestCompilePackagesEmitsAsyncChannelsSelectAndDefer(t *testing.T) {
|
|
|
3454
3677
|
"await $.chanSend($.pointerValue<Worker>(w).ch, v)",
|
|
3455
3678
|
"return await $.chanRecv($.pointerValue<Worker>(w).ch)",
|
|
3456
3679
|
"await using __defer = new $.AsyncDisposableStack()",
|
|
3457
|
-
"queueMicrotask(async () => { await (
|
|
3680
|
+
"queueMicrotask(async () => { await (async (): globalThis.Promise<void> => {",
|
|
3458
3681
|
"$.selectStatement<any, void>([",
|
|
3459
3682
|
"let v = __goscriptSelect1Result.value",
|
|
3460
3683
|
"return $.selectVoidReturn()",
|
|
@@ -4755,7 +4978,7 @@ func TestCompilePackagesLowersMethodValuesWithFixedParameters(t *testing.T) {
|
|
|
4755
4978
|
}
|
|
4756
4979
|
}
|
|
4757
4980
|
|
|
4758
|
-
func
|
|
4981
|
+
func TestCompilePackagesLowersSortSearchCallbackAsAsyncCompatible(t *testing.T) {
|
|
4759
4982
|
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
4760
4983
|
"go.mod": "module example.test/sync-callback\n\ngo 1.25.3\n",
|
|
4761
4984
|
"main.go": strings.Join([]string{
|
|
@@ -4794,11 +5017,14 @@ func TestCompilePackagesLowersSyncOverrideCallbackWithoutAsyncWrapper(t *testing
|
|
|
4794
5017
|
t.Fatal(err.Error())
|
|
4795
5018
|
}
|
|
4796
5019
|
text := string(content)
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
5020
|
+
for _, want := range []string{
|
|
5021
|
+
"return await sort.Search(",
|
|
5022
|
+
"$.functionValue(async (i: number): globalThis.Promise<boolean> => {",
|
|
5023
|
+
"let item = await $.pointerValue<Exclude<Items, null>>(items).Get(i)",
|
|
5024
|
+
} {
|
|
5025
|
+
if !strings.Contains(text, want) {
|
|
5026
|
+
t.Fatalf("missing %q in generated output:\n%s", want, text)
|
|
5027
|
+
}
|
|
4802
5028
|
}
|
|
4803
5029
|
}
|
|
4804
5030
|
|
|
@@ -5379,7 +5605,7 @@ func TestCompilePackagesLowersNamedStructConversionWithTypedAsyncFact(t *testing
|
|
|
5379
5605
|
if !strings.Contains(text, "$.markAsStructValue(new Target({Value: __goscriptConvert") {
|
|
5380
5606
|
t.Fatalf("missing typed named struct conversion target:\n%s", text)
|
|
5381
5607
|
}
|
|
5382
|
-
if !strings.Contains(text, "const __goscriptConvert1 = await (
|
|
5608
|
+
if !strings.Contains(text, "const __goscriptConvert1 = await (async ") {
|
|
5383
5609
|
t.Fatalf("missing async fact from function literal conversion source:\n%s", text)
|
|
5384
5610
|
}
|
|
5385
5611
|
}
|
package/compiler/wasm/compile.go
CHANGED
package/dist/compiler/index.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ export interface CompileConfig {
|
|
|
8
8
|
output?: string;
|
|
9
9
|
/** The working directory for the compiler. Defaults to the current working directory. */
|
|
10
10
|
dir?: string;
|
|
11
|
+
/** Compile all transitive dependencies of the requested package. */
|
|
12
|
+
allDependencies?: boolean;
|
|
13
|
+
/** Go import paths to reject from the compiled package graph. */
|
|
14
|
+
packageBlocklist?: string[] | string;
|
|
11
15
|
/** The path to the goscript executable. Defaults to `go run ./cmd/goscript`. */
|
|
12
16
|
goscriptPath?: string;
|
|
13
17
|
}
|