goscript 0.0.25 → 0.0.28
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 +4 -4
- package/cmd/goscript/cmd_compile.go +0 -3
- package/cmd/goscript/deps.go +11 -0
- package/compiler/analysis.go +259 -55
- package/compiler/assignment.go +2 -2
- package/compiler/builtin_test.go +1 -1
- package/compiler/compiler.go +201 -49
- package/compiler/compiler_test.go +53 -0
- package/compiler/composite-lit.go +32 -8
- package/compiler/decl.go +6 -6
- package/compiler/expr-call.go +83 -0
- package/compiler/expr.go +1 -1
- package/compiler/protobuf.go +557 -0
- package/compiler/spec-struct.go +4 -0
- package/compiler/spec-value.go +11 -3
- package/compiler/spec.go +18 -1
- package/compiler/stmt-assign.go +35 -0
- package/compiler/type-assert.go +87 -0
- package/compiler/type.go +5 -2
- package/dist/gs/builtin/builtin.d.ts +19 -1
- package/dist/gs/builtin/builtin.js +85 -5
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/channel.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +1 -1
- package/dist/gs/builtin/slice.js +59 -26
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/cmp/index.js.map +1 -1
- package/dist/gs/context/context.d.ts +1 -1
- package/dist/gs/context/context.js +20 -11
- package/dist/gs/context/context.js.map +1 -1
- package/dist/gs/errors/errors.d.ts +7 -0
- package/dist/gs/errors/errors.js +190 -0
- package/dist/gs/errors/errors.js.map +1 -0
- package/dist/gs/errors/index.d.ts +1 -0
- package/dist/gs/errors/index.js +2 -0
- package/dist/gs/errors/index.js.map +1 -0
- package/dist/gs/internal/goarch/index.js +1 -1
- package/dist/gs/internal/goarch/index.js.map +1 -1
- package/dist/gs/io/index.d.ts +1 -0
- package/dist/gs/io/index.js +2 -0
- package/dist/gs/io/index.js.map +1 -0
- package/dist/gs/io/io.d.ts +107 -0
- package/dist/gs/io/io.js +385 -0
- package/dist/gs/io/io.js.map +1 -0
- package/dist/gs/iter/iter.js.map +1 -1
- package/dist/gs/math/bits/index.js +34 -32
- package/dist/gs/math/bits/index.js.map +1 -1
- package/dist/gs/runtime/runtime.d.ts +1 -0
- package/dist/gs/runtime/runtime.js +15 -18
- package/dist/gs/runtime/runtime.js.map +1 -1
- package/dist/gs/slices/slices.d.ts +1 -1
- package/dist/gs/slices/slices.js +1 -1
- package/dist/gs/slices/slices.js.map +1 -1
- package/dist/gs/strings/builder.d.ts +18 -0
- package/dist/gs/strings/builder.js +205 -0
- package/dist/gs/strings/builder.js.map +1 -0
- package/dist/gs/strings/clone.d.ts +1 -0
- package/dist/gs/strings/clone.js +16 -0
- package/dist/gs/strings/clone.js.map +1 -0
- package/dist/gs/strings/compare.d.ts +1 -0
- package/dist/gs/strings/compare.js +14 -0
- package/dist/gs/strings/compare.js.map +1 -0
- package/dist/gs/strings/index.d.ts +2 -0
- package/dist/gs/strings/index.js +3 -0
- package/dist/gs/strings/index.js.map +1 -0
- package/dist/gs/strings/iter.d.ts +8 -0
- package/dist/gs/strings/iter.js +160 -0
- package/dist/gs/strings/iter.js.map +1 -0
- package/dist/gs/strings/reader.d.ts +34 -0
- package/dist/gs/strings/reader.js +418 -0
- package/dist/gs/strings/reader.js.map +1 -0
- package/dist/gs/strings/replace.d.ts +106 -0
- package/dist/gs/strings/replace.js +1136 -0
- package/dist/gs/strings/replace.js.map +1 -0
- package/dist/gs/strings/search.d.ts +24 -0
- package/dist/gs/strings/search.js +169 -0
- package/dist/gs/strings/search.js.map +1 -0
- package/dist/gs/strings/strings.d.ts +47 -0
- package/dist/gs/strings/strings.js +418 -0
- package/dist/gs/strings/strings.js.map +1 -0
- package/dist/gs/stringslite/index.d.ts +1 -0
- package/dist/gs/stringslite/index.js +2 -0
- package/dist/gs/stringslite/index.js.map +1 -0
- package/dist/gs/stringslite/strings.d.ts +11 -0
- package/dist/gs/stringslite/strings.js +67 -0
- package/dist/gs/stringslite/strings.js.map +1 -0
- package/dist/gs/sync/index.d.ts +1 -0
- package/dist/gs/sync/index.js +2 -0
- package/dist/gs/sync/index.js.map +1 -0
- package/dist/gs/sync/sync.d.ts +79 -0
- package/dist/gs/sync/sync.js +392 -0
- package/dist/gs/sync/sync.js.map +1 -0
- package/dist/gs/time/time.js +7 -7
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unicode/index.d.ts +1 -0
- package/dist/gs/unicode/index.js +2 -0
- package/dist/gs/unicode/index.js.map +1 -0
- package/dist/gs/unicode/unicode.d.ts +105 -0
- package/dist/gs/unicode/unicode.js +332 -0
- package/dist/gs/unicode/unicode.js.map +1 -0
- package/dist/gs/unicode/utf8/index.d.ts +1 -0
- package/dist/gs/unicode/utf8/index.js +3 -0
- package/dist/gs/unicode/utf8/index.js.map +1 -0
- package/dist/gs/unicode/utf8/utf8.d.ts +20 -0
- package/dist/gs/unicode/utf8/utf8.js +196 -0
- package/dist/gs/unicode/utf8/utf8.js.map +1 -0
- package/dist/gs/unsafe/index.d.ts +1 -0
- package/dist/gs/unsafe/index.js +2 -0
- package/dist/gs/unsafe/index.js.map +1 -0
- package/dist/gs/unsafe/unsafe.d.ts +11 -0
- package/dist/gs/unsafe/unsafe.js +44 -0
- package/dist/gs/unsafe/unsafe.js.map +1 -0
- package/go.mod +2 -1
- package/go.sum +6 -2
- package/gs/README.md +6 -0
- package/gs/builtin/builtin.ts +158 -0
- package/gs/builtin/channel.ts +683 -0
- package/gs/builtin/defer.ts +58 -0
- package/gs/builtin/index.ts +1 -0
- package/gs/builtin/io.ts +22 -0
- package/gs/builtin/map.ts +50 -0
- package/gs/builtin/slice.ts +1030 -0
- package/gs/builtin/type.ts +1106 -0
- package/gs/builtin/varRef.ts +25 -0
- package/gs/cmp/godoc.txt +8 -0
- package/gs/cmp/index.ts +29 -0
- package/gs/context/context.ts +401 -0
- package/gs/context/godoc.txt +69 -0
- package/gs/context/index.ts +1 -0
- package/gs/errors/errors.ts +223 -0
- package/gs/errors/godoc.txt +63 -0
- package/gs/errors/index.ts +1 -0
- package/gs/internal/goarch/godoc.txt +39 -0
- package/gs/internal/goarch/index.ts +18 -0
- package/gs/io/godoc.txt +61 -0
- package/gs/io/index.ts +1 -0
- package/gs/io/io.go +75 -0
- package/gs/io/io.ts +546 -0
- package/gs/iter/godoc.txt +203 -0
- package/gs/iter/index.ts +1 -0
- package/gs/iter/iter.ts +117 -0
- package/gs/math/bits/index.ts +356 -0
- package/gs/math/godoc.txt +76 -0
- package/gs/runtime/godoc.txt +331 -0
- package/gs/runtime/index.ts +1 -0
- package/gs/runtime/runtime.ts +178 -0
- package/gs/slices/godoc.txt +44 -0
- package/gs/slices/index.ts +1 -0
- package/gs/slices/slices.ts +22 -0
- package/gs/strings/builder.test.ts +121 -0
- package/gs/strings/builder.ts +223 -0
- package/gs/strings/clone.test.ts +43 -0
- package/gs/strings/clone.ts +17 -0
- package/gs/strings/compare.test.ts +84 -0
- package/gs/strings/compare.ts +13 -0
- package/gs/strings/godoc.txt +66 -0
- package/gs/strings/index.ts +2 -0
- package/gs/strings/iter.test.ts +343 -0
- package/gs/strings/iter.ts +171 -0
- package/gs/strings/reader.test.ts +243 -0
- package/gs/strings/reader.ts +451 -0
- package/gs/strings/replace.test.ts +181 -0
- package/gs/strings/replace.ts +1310 -0
- package/gs/strings/search.test.ts +214 -0
- package/gs/strings/search.ts +213 -0
- package/gs/strings/strings.test.ts +477 -0
- package/gs/strings/strings.ts +510 -0
- package/gs/stringslite/godoc.txt +17 -0
- package/gs/stringslite/index.ts +1 -0
- package/gs/stringslite/strings.ts +82 -0
- package/gs/sync/godoc.txt +21 -0
- package/gs/sync/index.ts +1 -0
- package/gs/sync/sync.go +64 -0
- package/gs/sync/sync.ts +449 -0
- package/gs/time/godoc.md +116 -0
- package/gs/time/godoc.txt +116 -0
- package/gs/time/index.ts +1 -0
- package/gs/time/time.ts +272 -0
- package/gs/unicode/godoc.txt +52 -0
- package/gs/unicode/index.ts +1 -0
- package/gs/unicode/unicode.go +38 -0
- package/gs/unicode/unicode.ts +418 -0
- package/gs/unicode/utf8/godoc.txt +22 -0
- package/gs/unicode/utf8/index.ts +2 -0
- package/gs/unicode/utf8/utf8.ts +227 -0
- package/gs/unsafe/godoc.txt +19 -0
- package/gs/unsafe/index.ts +1 -0
- package/gs/unsafe/unsafe.test.ts +68 -0
- package/gs/unsafe/unsafe.ts +77 -0
- package/package.json +6 -4
package/compiler/compiler.go
CHANGED
|
@@ -4,15 +4,15 @@ import (
|
|
|
4
4
|
"context"
|
|
5
5
|
"fmt"
|
|
6
6
|
"go/ast"
|
|
7
|
+
"go/constant"
|
|
7
8
|
"go/token"
|
|
8
9
|
"go/types"
|
|
9
10
|
"os"
|
|
10
11
|
"path/filepath"
|
|
11
12
|
"slices"
|
|
13
|
+
"sort"
|
|
12
14
|
"strings"
|
|
13
15
|
|
|
14
|
-
"go/constant"
|
|
15
|
-
|
|
16
16
|
gs "github.com/aperturerobotics/goscript"
|
|
17
17
|
"github.com/sirupsen/logrus"
|
|
18
18
|
"golang.org/x/tools/go/packages"
|
|
@@ -146,6 +146,18 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
|
|
|
146
146
|
|
|
147
147
|
// Visit all imports, including standard library packages
|
|
148
148
|
for _, imp := range pkg.Imports {
|
|
149
|
+
// Skip protobuf-go-lite packages and their dependencies
|
|
150
|
+
if isProtobufGoLitePackage(imp.PkgPath) {
|
|
151
|
+
c.le.Debugf("Skipping protobuf-go-lite package: %s", imp.PkgPath)
|
|
152
|
+
continue
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Skip packages that are only used by .pb.go files
|
|
156
|
+
if isPackageOnlyUsedByProtobufFiles(pkg, imp.PkgPath) {
|
|
157
|
+
c.le.Debugf("Skipping package only used by .pb.go files: %s", imp.PkgPath)
|
|
158
|
+
continue
|
|
159
|
+
}
|
|
160
|
+
|
|
149
161
|
visit(imp)
|
|
150
162
|
}
|
|
151
163
|
}
|
|
@@ -239,29 +251,6 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
|
|
|
239
251
|
continue
|
|
240
252
|
}
|
|
241
253
|
|
|
242
|
-
// Check if this is the unsafe package, which is not supported in GoScript
|
|
243
|
-
if pkg.PkgPath == "unsafe" {
|
|
244
|
-
// Find which package depends on unsafe by looking at the import graph
|
|
245
|
-
var dependentPackages []string
|
|
246
|
-
for _, otherPkg := range pkgs {
|
|
247
|
-
if otherPkg.PkgPath != "unsafe" {
|
|
248
|
-
for importPath := range otherPkg.Imports {
|
|
249
|
-
if importPath == "unsafe" {
|
|
250
|
-
dependentPackages = append(dependentPackages, otherPkg.PkgPath)
|
|
251
|
-
break
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
dependentList := "unknown package"
|
|
258
|
-
if len(dependentPackages) > 0 {
|
|
259
|
-
dependentList = strings.Join(dependentPackages, ", ")
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return nil, fmt.Errorf("cannot compile package 'unsafe': GoScript does not support the unsafe package due to its low-level memory operations that are incompatible with TypeScript/JavaScript. This package is required by: %s. Consider using alternative approaches that don't require unsafe operations", dependentList)
|
|
263
|
-
}
|
|
264
|
-
|
|
265
254
|
pkgCompiler, err := NewPackageCompiler(c.le, &c.config, pkg)
|
|
266
255
|
if err != nil {
|
|
267
256
|
return nil, fmt.Errorf("failed to create package compiler for %s: %w", pkg.PkgPath, err)
|
|
@@ -326,6 +315,9 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
|
|
|
326
315
|
}
|
|
327
316
|
}
|
|
328
317
|
|
|
318
|
+
// Perform package-level analysis for auto-imports
|
|
319
|
+
packageAnalysis := AnalyzePackage(c.pkg)
|
|
320
|
+
|
|
329
321
|
// Track all compiled files for later generating the index.ts
|
|
330
322
|
compiledFiles := make([]string, 0, len(c.pkg.CompiledGoFiles))
|
|
331
323
|
|
|
@@ -337,13 +329,35 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
|
|
|
337
329
|
return err
|
|
338
330
|
}
|
|
339
331
|
|
|
332
|
+
// Check if this is a .pb.go file that should be skipped
|
|
333
|
+
baseFileName := filepath.Base(fileName)
|
|
334
|
+
if strings.HasSuffix(baseFileName, ".pb.go") {
|
|
335
|
+
// Check if there's a corresponding .pb.ts file
|
|
336
|
+
pbTsFileName := strings.TrimSuffix(baseFileName, ".pb.go") + ".pb.ts"
|
|
337
|
+
packageDir := filepath.Dir(fileName)
|
|
338
|
+
pbTsPath := filepath.Join(packageDir, pbTsFileName)
|
|
339
|
+
|
|
340
|
+
if _, err := os.Stat(pbTsPath); err == nil {
|
|
341
|
+
// .pb.ts file exists, copy it instead of transpiling .pb.go
|
|
342
|
+
c.le.WithField("file", relWdFileName).Debug("found .pb.ts file, copying instead of transpiling .pb.go")
|
|
343
|
+
|
|
344
|
+
if err := c.copyProtobufTSFile(pbTsPath, pbTsFileName); err != nil {
|
|
345
|
+
return fmt.Errorf("failed to copy protobuf .pb.ts file: %w", err)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Add the .pb file to our compiled files list for index generation
|
|
349
|
+
pbFileName := strings.TrimSuffix(baseFileName, ".pb.go") + ".pb"
|
|
350
|
+
compiledFiles = append(compiledFiles, pbFileName)
|
|
351
|
+
continue // Skip transpiling this .pb.go file
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
340
355
|
c.le.WithField("file", relWdFileName).Debug("compiling file")
|
|
341
|
-
if err := c.CompileFile(ctx, fileName, f); err != nil {
|
|
356
|
+
if err := c.CompileFile(ctx, fileName, f, packageAnalysis); err != nil {
|
|
342
357
|
return err
|
|
343
358
|
}
|
|
344
359
|
|
|
345
360
|
// Add the base filename to our list for the index.ts generation
|
|
346
|
-
baseFileName := filepath.Base(fileName)
|
|
347
361
|
// Strip .go extension and add .gs
|
|
348
362
|
gsFileName := strings.TrimSuffix(baseFileName, ".go") + ".gs"
|
|
349
363
|
compiledFiles = append(compiledFiles, gsFileName)
|
|
@@ -358,8 +372,9 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
|
|
|
358
372
|
}
|
|
359
373
|
|
|
360
374
|
// generateIndexFile creates an index.ts file in the package output directory
|
|
361
|
-
// that re-exports
|
|
362
|
-
// This ensures the package can be imported correctly by TypeScript modules
|
|
375
|
+
// that re-exports only Go-exported symbols from the compiled TypeScript files.
|
|
376
|
+
// This ensures the package can be imported correctly by TypeScript modules
|
|
377
|
+
// while maintaining proper Go package boundaries.
|
|
363
378
|
func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
364
379
|
indexFilePath := filepath.Join(c.outputPath, "index.ts")
|
|
365
380
|
|
|
@@ -370,12 +385,128 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
370
385
|
}
|
|
371
386
|
defer indexFile.Close() //nolint:errcheck
|
|
372
387
|
|
|
373
|
-
//
|
|
388
|
+
// Collect exported symbols from all files in the package
|
|
389
|
+
var exportedSymbols []string
|
|
390
|
+
|
|
391
|
+
// Iterate through all syntax files to find exported symbols
|
|
392
|
+
for i, syntax := range c.pkg.Syntax {
|
|
393
|
+
fileName := c.pkg.CompiledGoFiles[i]
|
|
394
|
+
baseFileName := filepath.Base(fileName)
|
|
395
|
+
gsFileName := strings.TrimSuffix(baseFileName, ".go") + ".gs"
|
|
396
|
+
|
|
397
|
+
// Only include this file if it was compiled (in our compiledFiles list)
|
|
398
|
+
fileWasCompiled := false
|
|
399
|
+
for _, compiledFile := range compiledFiles {
|
|
400
|
+
if compiledFile == gsFileName {
|
|
401
|
+
fileWasCompiled = true
|
|
402
|
+
break
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if !fileWasCompiled {
|
|
406
|
+
continue
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Analyze declarations in this file to find exported symbols
|
|
410
|
+
for _, decl := range syntax.Decls {
|
|
411
|
+
switch d := decl.(type) {
|
|
412
|
+
case *ast.FuncDecl:
|
|
413
|
+
// Only include top-level functions (not methods)
|
|
414
|
+
if d.Recv == nil && d.Name.IsExported() {
|
|
415
|
+
exportedSymbols = append(exportedSymbols, d.Name.Name)
|
|
416
|
+
}
|
|
417
|
+
case *ast.GenDecl:
|
|
418
|
+
for _, spec := range d.Specs {
|
|
419
|
+
switch s := spec.(type) {
|
|
420
|
+
case *ast.TypeSpec:
|
|
421
|
+
if s.Name.IsExported() {
|
|
422
|
+
exportedSymbols = append(exportedSymbols, s.Name.Name)
|
|
423
|
+
}
|
|
424
|
+
case *ast.ValueSpec:
|
|
425
|
+
for _, name := range s.Names {
|
|
426
|
+
if name.IsExported() {
|
|
427
|
+
exportedSymbols = append(exportedSymbols, name.Name)
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Remove duplicates and sort
|
|
437
|
+
symbolMap := make(map[string]bool)
|
|
438
|
+
for _, symbol := range exportedSymbols {
|
|
439
|
+
symbolMap[symbol] = true
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
var uniqueSymbols []string
|
|
443
|
+
for symbol := range symbolMap {
|
|
444
|
+
uniqueSymbols = append(uniqueSymbols, symbol)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Sort for consistent output
|
|
448
|
+
sort.Strings(uniqueSymbols)
|
|
449
|
+
|
|
450
|
+
// Write selective re-exports for each compiled file
|
|
374
451
|
for _, fileName := range compiledFiles {
|
|
375
|
-
//
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
452
|
+
// Check if this is a protobuf file
|
|
453
|
+
if strings.HasSuffix(fileName, ".pb") {
|
|
454
|
+
// For protobuf files, add a simple re-export
|
|
455
|
+
pbTsFileName := fileName + ".ts"
|
|
456
|
+
if err := c.writeProtobufExports(indexFile, fileName, pbTsFileName); err != nil {
|
|
457
|
+
return err
|
|
458
|
+
}
|
|
459
|
+
continue
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Find which symbols this file exports
|
|
463
|
+
var fileSymbols []string
|
|
464
|
+
|
|
465
|
+
// Find the corresponding syntax file
|
|
466
|
+
for i, syntax := range c.pkg.Syntax {
|
|
467
|
+
syntaxFileName := c.pkg.CompiledGoFiles[i]
|
|
468
|
+
syntaxBaseFileName := filepath.Base(syntaxFileName)
|
|
469
|
+
syntaxGsFileName := strings.TrimSuffix(syntaxBaseFileName, ".go") + ".gs"
|
|
470
|
+
|
|
471
|
+
if syntaxGsFileName != fileName {
|
|
472
|
+
continue
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Collect exported symbols from this specific file
|
|
476
|
+
for _, decl := range syntax.Decls {
|
|
477
|
+
switch d := decl.(type) {
|
|
478
|
+
case *ast.FuncDecl:
|
|
479
|
+
if d.Recv == nil && d.Name.IsExported() {
|
|
480
|
+
fileSymbols = append(fileSymbols, d.Name.Name)
|
|
481
|
+
}
|
|
482
|
+
case *ast.GenDecl:
|
|
483
|
+
for _, spec := range d.Specs {
|
|
484
|
+
switch s := spec.(type) {
|
|
485
|
+
case *ast.TypeSpec:
|
|
486
|
+
if s.Name.IsExported() {
|
|
487
|
+
fileSymbols = append(fileSymbols, s.Name.Name)
|
|
488
|
+
}
|
|
489
|
+
case *ast.ValueSpec:
|
|
490
|
+
for _, name := range s.Names {
|
|
491
|
+
if name.IsExported() {
|
|
492
|
+
fileSymbols = append(fileSymbols, name.Name)
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
break
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Write selective export if this file has exported symbols
|
|
503
|
+
if len(fileSymbols) > 0 {
|
|
504
|
+
sort.Strings(fileSymbols)
|
|
505
|
+
exportLine := fmt.Sprintf("export { %s } from \"./%s.js\"\n",
|
|
506
|
+
strings.Join(fileSymbols, ", "), fileName)
|
|
507
|
+
if _, err := indexFile.WriteString(exportLine); err != nil {
|
|
508
|
+
return err
|
|
509
|
+
}
|
|
379
510
|
}
|
|
380
511
|
}
|
|
381
512
|
|
|
@@ -388,7 +519,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
388
519
|
// about varRefing, async functions, defer statements).
|
|
389
520
|
// Then, it creates a `FileCompiler` instance for the file and invokes its
|
|
390
521
|
// `Compile` method to generate the TypeScript code.
|
|
391
|
-
func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *ast.File) error {
|
|
522
|
+
func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *ast.File, packageAnalysis *PackageAnalysis) error {
|
|
392
523
|
// Create a new analysis instance for per-file data
|
|
393
524
|
analysis := NewAnalysis()
|
|
394
525
|
|
|
@@ -398,7 +529,7 @@ func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *
|
|
|
398
529
|
// Analyze the file before compiling
|
|
399
530
|
AnalyzeFile(syntax, p.pkg, analysis, cmap)
|
|
400
531
|
|
|
401
|
-
fileCompiler, err := NewFileCompiler(p.compilerConf, p.pkg, syntax, name, analysis)
|
|
532
|
+
fileCompiler, err := NewFileCompiler(p.compilerConf, p.pkg, syntax, name, analysis, packageAnalysis)
|
|
402
533
|
if err != nil {
|
|
403
534
|
return err
|
|
404
535
|
}
|
|
@@ -410,12 +541,13 @@ func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *
|
|
|
410
541
|
// initializes the `TSCodeWriter` for TypeScript code generation, and uses a
|
|
411
542
|
// `GoToTSCompiler` to translate Go declarations and statements.
|
|
412
543
|
type FileCompiler struct {
|
|
413
|
-
compilerConfig
|
|
414
|
-
codeWriter
|
|
415
|
-
pkg
|
|
416
|
-
ast
|
|
417
|
-
fullPath
|
|
418
|
-
Analysis
|
|
544
|
+
compilerConfig *Config
|
|
545
|
+
codeWriter *TSCodeWriter
|
|
546
|
+
pkg *packages.Package
|
|
547
|
+
ast *ast.File
|
|
548
|
+
fullPath string
|
|
549
|
+
Analysis *Analysis
|
|
550
|
+
PackageAnalysis *PackageAnalysis
|
|
419
551
|
}
|
|
420
552
|
|
|
421
553
|
// NewFileCompiler creates a new `FileCompiler` for a specific Go file.
|
|
@@ -428,13 +560,15 @@ func NewFileCompiler(
|
|
|
428
560
|
astFile *ast.File,
|
|
429
561
|
fullPath string,
|
|
430
562
|
analysis *Analysis,
|
|
563
|
+
packageAnalysis *PackageAnalysis,
|
|
431
564
|
) (*FileCompiler, error) {
|
|
432
565
|
return &FileCompiler{
|
|
433
|
-
compilerConfig:
|
|
434
|
-
pkg:
|
|
435
|
-
ast:
|
|
436
|
-
fullPath:
|
|
437
|
-
Analysis:
|
|
566
|
+
compilerConfig: compilerConf,
|
|
567
|
+
pkg: pkg,
|
|
568
|
+
ast: astFile,
|
|
569
|
+
fullPath: fullPath,
|
|
570
|
+
Analysis: analysis,
|
|
571
|
+
PackageAnalysis: packageAnalysis,
|
|
438
572
|
}, nil
|
|
439
573
|
}
|
|
440
574
|
|
|
@@ -468,7 +602,26 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
|
|
|
468
602
|
|
|
469
603
|
// Add import for the goscript runtime using namespace import and alias
|
|
470
604
|
c.codeWriter.WriteLinef("import * as $ from %q;", "@goscript/builtin/builtin.js")
|
|
471
|
-
|
|
605
|
+
|
|
606
|
+
// Check if there are any .pb.go files in this package and add imports for them
|
|
607
|
+
if err := c.addProtobufImports(); err != nil {
|
|
608
|
+
return fmt.Errorf("failed to add protobuf imports: %w", err)
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Generate auto-imports for functions from other files in the same package
|
|
612
|
+
currentFileName := strings.TrimSuffix(filepath.Base(c.fullPath), ".go")
|
|
613
|
+
if imports := c.PackageAnalysis.FunctionCalls[currentFileName]; imports != nil {
|
|
614
|
+
for sourceFile, functions := range imports {
|
|
615
|
+
if len(functions) > 0 {
|
|
616
|
+
// Sort functions for consistent output
|
|
617
|
+
sort.Strings(functions)
|
|
618
|
+
c.codeWriter.WriteLinef("import { %s } from \"./%s.gs.js\";",
|
|
619
|
+
strings.Join(functions, ", "), sourceFile)
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
c.codeWriter.WriteLine("") // Add a newline after imports
|
|
472
625
|
|
|
473
626
|
if err := goWriter.WriteDecls(f.Decls); err != nil {
|
|
474
627
|
return fmt.Errorf("failed to write declarations: %w", err)
|
|
@@ -483,7 +636,6 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
|
|
|
483
636
|
// decisions about code generation (e.g., varRefing, async behavior).
|
|
484
637
|
type GoToTSCompiler struct {
|
|
485
638
|
tsw *TSCodeWriter
|
|
486
|
-
|
|
487
639
|
pkg *packages.Package
|
|
488
640
|
|
|
489
641
|
analysis *Analysis
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package compiler_test
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"context"
|
|
4
5
|
"os"
|
|
5
6
|
"os/exec"
|
|
6
7
|
"path/filepath"
|
|
@@ -11,7 +12,9 @@ import (
|
|
|
11
12
|
"sync/atomic"
|
|
12
13
|
"testing"
|
|
13
14
|
|
|
15
|
+
"github.com/aperturerobotics/goscript/compiler"
|
|
14
16
|
"github.com/aperturerobotics/goscript/compliance"
|
|
17
|
+
"github.com/sirupsen/logrus"
|
|
15
18
|
)
|
|
16
19
|
|
|
17
20
|
// NOTE: this is here instead of compliance/compliance_test.go so coverage ends up in this package.
|
|
@@ -137,3 +140,53 @@ func getParentGoModulePath() (string, error) {
|
|
|
137
140
|
}
|
|
138
141
|
return strings.TrimSpace(string(output)), nil
|
|
139
142
|
}
|
|
143
|
+
|
|
144
|
+
func TestUnsafePackageCompilation(t *testing.T) {
|
|
145
|
+
// Create a temporary directory for the test output
|
|
146
|
+
tempDir, err := os.MkdirTemp("", "goscript-test-unsafe")
|
|
147
|
+
if err != nil {
|
|
148
|
+
t.Fatalf("Failed to create temp dir: %v", err)
|
|
149
|
+
}
|
|
150
|
+
defer os.RemoveAll(tempDir)
|
|
151
|
+
|
|
152
|
+
// Setup logger
|
|
153
|
+
log := logrus.New()
|
|
154
|
+
log.SetLevel(logrus.DebugLevel)
|
|
155
|
+
le := logrus.NewEntry(log)
|
|
156
|
+
|
|
157
|
+
// Test with AllDependencies=true and DisableEmitBuiltin=false to ensure handwritten packages are copied
|
|
158
|
+
config := &compiler.Config{
|
|
159
|
+
OutputPath: tempDir,
|
|
160
|
+
AllDependencies: true,
|
|
161
|
+
DisableEmitBuiltin: false, // This ensures handwritten packages are copied to output
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
comp, err := compiler.NewCompiler(config, le, nil)
|
|
165
|
+
if err != nil {
|
|
166
|
+
t.Fatalf("Failed to create compiler: %v", err)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Try to compile a package that has dependencies that import unsafe
|
|
170
|
+
// We'll use "sync/atomic" which imports unsafe but doesn't have a handwritten equivalent
|
|
171
|
+
result, err := comp.CompilePackages(context.Background(), "sync/atomic")
|
|
172
|
+
// This should now succeed since we have a handwritten unsafe package
|
|
173
|
+
if err != nil {
|
|
174
|
+
t.Fatalf("Expected compilation to succeed with handwritten unsafe package, but it failed: %v", err)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Verify that the unsafe package was copied (not compiled) since it has a handwritten equivalent
|
|
178
|
+
if !slices.Contains(result.CopiedPackages, "unsafe") {
|
|
179
|
+
t.Errorf("Expected unsafe package to be in CopiedPackages, but it wasn't. CopiedPackages: %v", result.CopiedPackages)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Verify that sync/atomic was compiled
|
|
183
|
+
if !slices.Contains(result.CompiledPackages, "sync/atomic") {
|
|
184
|
+
t.Errorf("Expected sync/atomic package to be in CompiledPackages, but it wasn't. CompiledPackages: %v", result.CompiledPackages)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Check that the unsafe package directory exists in the output
|
|
188
|
+
unsafePath := filepath.Join(tempDir, "@goscript/unsafe")
|
|
189
|
+
if _, err := os.Stat(unsafePath); os.IsNotExist(err) {
|
|
190
|
+
t.Errorf("unsafe package directory was not created at %s", unsafePath)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -199,18 +199,34 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
199
199
|
if underlyingStruct, ok := namedType.Underlying().(*types.Struct); ok {
|
|
200
200
|
structType = underlyingStruct
|
|
201
201
|
isStructLiteral = true
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
c.
|
|
202
|
+
|
|
203
|
+
// Check if this is a protobuf type
|
|
204
|
+
if handled, err := c.writeProtobufCompositeLit(exp, litType); handled {
|
|
205
|
+
if err != nil {
|
|
206
|
+
return err
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
// Named struct, use constructor
|
|
210
|
+
c.tsw.WriteLiterally("new ")
|
|
211
|
+
c.WriteTypeExpr(exp.Type)
|
|
212
|
+
}
|
|
205
213
|
}
|
|
206
214
|
} else if ptrType, ok := litType.(*types.Pointer); ok {
|
|
207
215
|
if namedElem, ok := ptrType.Elem().(*types.Named); ok {
|
|
208
216
|
if underlyingStruct, ok := namedElem.Underlying().(*types.Struct); ok {
|
|
209
217
|
structType = underlyingStruct
|
|
210
218
|
isStructLiteral = true // Treat pointer-to-struct literal similarly
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
c.
|
|
219
|
+
|
|
220
|
+
// Check if this is a protobuf type
|
|
221
|
+
if handled, err := c.writeProtobufCompositeLit(exp, litType); handled {
|
|
222
|
+
if err != nil {
|
|
223
|
+
return err
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
// Named struct pointer, use constructor
|
|
227
|
+
c.tsw.WriteLiterally("new ")
|
|
228
|
+
c.WriteTypeExpr(exp.Type)
|
|
229
|
+
}
|
|
214
230
|
}
|
|
215
231
|
}
|
|
216
232
|
} else if underlyingStruct, ok := litType.Underlying().(*types.Struct); ok {
|
|
@@ -339,7 +355,11 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
339
355
|
if firstFieldWritten {
|
|
340
356
|
c.tsw.WriteLiterally(", ")
|
|
341
357
|
}
|
|
342
|
-
|
|
358
|
+
|
|
359
|
+
// Convert field name for protobuf types
|
|
360
|
+
fieldName := c.convertProtobufFieldNameInLiteral(keyName, litType)
|
|
361
|
+
|
|
362
|
+
c.tsw.WriteLiterally(fieldName)
|
|
343
363
|
c.tsw.WriteLiterally(": ")
|
|
344
364
|
if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
|
|
345
365
|
return err
|
|
@@ -538,7 +558,11 @@ func (c *GoToTSCompiler) writeUntypedStructLiteral(exp *ast.CompositeLit, struct
|
|
|
538
558
|
if firstFieldWritten {
|
|
539
559
|
c.tsw.WriteLiterally(", ")
|
|
540
560
|
}
|
|
541
|
-
|
|
561
|
+
|
|
562
|
+
// Convert field name for protobuf types
|
|
563
|
+
fieldName := c.convertProtobufFieldNameInLiteral(keyName, structType.Underlying())
|
|
564
|
+
|
|
565
|
+
c.tsw.WriteLiterally(fieldName)
|
|
542
566
|
c.tsw.WriteLiterally(": ")
|
|
543
567
|
if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
|
|
544
568
|
return err
|
package/compiler/decl.go
CHANGED
|
@@ -68,11 +68,9 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
|
|
|
68
68
|
c.WriteDoc(decl.Doc)
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
c.tsw.WriteLiterally("export ")
|
|
75
|
-
}
|
|
71
|
+
// Export all functions for intra-package visibility
|
|
72
|
+
// This allows other files in the same package to import functions
|
|
73
|
+
c.tsw.WriteLiterally("export ")
|
|
76
74
|
|
|
77
75
|
// Check if this function is async using the analysis data
|
|
78
76
|
var isAsync bool
|
|
@@ -233,7 +231,9 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
233
231
|
if recvName != "_" {
|
|
234
232
|
c.tsw.WriteLine("{")
|
|
235
233
|
c.tsw.Indent(1)
|
|
236
|
-
|
|
234
|
+
// Sanitize the receiver name to avoid conflicts with TypeScript reserved words
|
|
235
|
+
sanitizedRecvName := c.sanitizeIdentifier(recvName)
|
|
236
|
+
c.tsw.WriteLinef("const %s = this", sanitizedRecvName)
|
|
237
237
|
|
|
238
238
|
// Add using statement if needed
|
|
239
239
|
if c.analysis.NeedsDefer(decl.Body) {
|
package/compiler/expr-call.go
CHANGED
|
@@ -5,6 +5,7 @@ import (
|
|
|
5
5
|
"go/ast"
|
|
6
6
|
"go/token"
|
|
7
7
|
"go/types"
|
|
8
|
+
"strings"
|
|
8
9
|
|
|
9
10
|
"github.com/pkg/errors"
|
|
10
11
|
)
|
|
@@ -38,6 +39,11 @@ import (
|
|
|
38
39
|
func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
39
40
|
expFun := exp.Fun
|
|
40
41
|
|
|
42
|
+
// Handle protobuf method calls
|
|
43
|
+
if handled, err := c.writeProtobufMethodCall(exp); handled {
|
|
44
|
+
return err
|
|
45
|
+
}
|
|
46
|
+
|
|
41
47
|
// Handle any type conversion with nil argument
|
|
42
48
|
if len(exp.Args) == 1 {
|
|
43
49
|
if nilIdent, isIdent := exp.Args[0].(*ast.Ident); isIdent && nilIdent.Name == "nil" {
|
|
@@ -325,6 +331,53 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
325
331
|
}
|
|
326
332
|
}
|
|
327
333
|
}
|
|
334
|
+
} else {
|
|
335
|
+
// Handle named types with slice underlying types: make(NamedSliceType, len, cap)
|
|
336
|
+
// This handles cases like: type appendSliceWriter []byte; make(appendSliceWriter, 0, len(s))
|
|
337
|
+
namedType := typeName.Type()
|
|
338
|
+
if sliceType, isSlice := namedType.Underlying().(*types.Slice); isSlice {
|
|
339
|
+
goElemType := sliceType.Elem()
|
|
340
|
+
|
|
341
|
+
// Check if it's a named type with []byte underlying type
|
|
342
|
+
if basicElem, isBasic := goElemType.(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
|
|
343
|
+
c.tsw.WriteLiterally("new Uint8Array(")
|
|
344
|
+
if len(exp.Args) >= 2 {
|
|
345
|
+
if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
|
|
346
|
+
return err
|
|
347
|
+
}
|
|
348
|
+
// Capacity argument for make([]byte, len, cap) is ignored for new Uint8Array(len)
|
|
349
|
+
} else {
|
|
350
|
+
// If no length is provided, default to 0
|
|
351
|
+
c.tsw.WriteLiterally("0")
|
|
352
|
+
}
|
|
353
|
+
c.tsw.WriteLiterally(")")
|
|
354
|
+
return nil // Handled make for named []byte type
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Handle other named slice types
|
|
358
|
+
c.tsw.WriteLiterally("$.makeSlice<")
|
|
359
|
+
c.WriteGoType(goElemType, GoTypeContextGeneral) // Write the element type
|
|
360
|
+
c.tsw.WriteLiterally(">(")
|
|
361
|
+
|
|
362
|
+
if len(exp.Args) >= 2 {
|
|
363
|
+
if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
|
|
364
|
+
return err
|
|
365
|
+
}
|
|
366
|
+
if len(exp.Args) == 3 {
|
|
367
|
+
c.tsw.WriteLiterally(", ")
|
|
368
|
+
if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
|
|
369
|
+
return err
|
|
370
|
+
}
|
|
371
|
+
} else if len(exp.Args) > 3 {
|
|
372
|
+
return errors.New("makeSlice expects 2 or 3 arguments")
|
|
373
|
+
}
|
|
374
|
+
} else {
|
|
375
|
+
// If no length is provided, default to 0
|
|
376
|
+
c.tsw.WriteLiterally("0")
|
|
377
|
+
}
|
|
378
|
+
c.tsw.WriteLiterally(")")
|
|
379
|
+
return nil // Handled make for named slice type
|
|
380
|
+
}
|
|
328
381
|
}
|
|
329
382
|
}
|
|
330
383
|
}
|
|
@@ -554,6 +607,36 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
554
607
|
return nil // Handled regular function call
|
|
555
608
|
}
|
|
556
609
|
} else {
|
|
610
|
+
// Check if this is an async method call (e.g., mu.Lock())
|
|
611
|
+
if selExpr, ok := expFun.(*ast.SelectorExpr); ok {
|
|
612
|
+
// Check if this is a method call on a variable (e.g., mu.Lock())
|
|
613
|
+
if ident, ok := selExpr.X.(*ast.Ident); ok {
|
|
614
|
+
// Get the type of the receiver
|
|
615
|
+
if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
616
|
+
if varObj, ok := obj.(*types.Var); ok {
|
|
617
|
+
// Get the type name and package
|
|
618
|
+
if namedType, ok := varObj.Type().(*types.Named); ok {
|
|
619
|
+
typeName := namedType.Obj().Name()
|
|
620
|
+
methodName := selExpr.Sel.Name
|
|
621
|
+
|
|
622
|
+
// Check if the type is from an imported package
|
|
623
|
+
if typePkg := namedType.Obj().Pkg(); typePkg != nil && typePkg != c.pkg.Types {
|
|
624
|
+
pkgPath := typePkg.Path()
|
|
625
|
+
// Extract package name from path (e.g., "sync" from "github.com/.../gs/sync")
|
|
626
|
+
parts := strings.Split(pkgPath, "/")
|
|
627
|
+
pkgName := parts[len(parts)-1]
|
|
628
|
+
|
|
629
|
+
// Check if this method is async based on metadata
|
|
630
|
+
if c.analysis.IsMethodAsync(pkgName, typeName, methodName) {
|
|
631
|
+
c.tsw.WriteLiterally("await ")
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
557
640
|
// If expFun is a function literal, it needs to be wrapped in parentheses for IIFE syntax
|
|
558
641
|
if _, isFuncLit := expFun.(*ast.FuncLit); isFuncLit {
|
|
559
642
|
c.tsw.WriteLiterally("(")
|
package/compiler/expr.go
CHANGED
|
@@ -388,7 +388,7 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
388
388
|
isBitwise = true
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
-
//
|
|
391
|
+
// Handle large bit shift expressions that would overflow in JavaScript
|
|
392
392
|
if exp.Op == token.SHL {
|
|
393
393
|
// Check if this is 1 << 63 pattern
|
|
394
394
|
if leftLit, leftIsLit := exp.X.(*ast.BasicLit); leftIsLit && leftLit.Value == "1" {
|