🌐 AI搜索 & 代理 主页
Skip to content

Commit 32d7337

Browse files
Merge pull request #1393 from Workiva/srcMapPrelude
Adding JS writer to source map filter
2 parents 1e789f1 + ee125f6 commit 32d7337

File tree

16 files changed

+333
-118
lines changed

16 files changed

+333
-118
lines changed

build/build.go

Lines changed: 5 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"time"
2626

2727
"github.com/fsnotify/fsnotify"
28-
"github.com/neelance/sourcemap"
2928
log "github.com/sirupsen/logrus"
3029
"golang.org/x/tools/go/buildutil"
3130

@@ -1175,12 +1174,6 @@ func (s *Session) compilePackage(srcs *sources.Sources, tContext *types.Context)
11751174
return nil, err
11761175
}
11771176

1178-
for _, jsFile := range srcs.JSFiles {
1179-
archive.IncJSCode = append(archive.IncJSCode, []byte("\t(function() {\n")...)
1180-
archive.IncJSCode = append(archive.IncJSCode, jsFile.Content...)
1181-
archive.IncJSCode = append(archive.IncJSCode, []byte("\n\t}).call($global);\n")...)
1182-
}
1183-
11841177
if s.options.Verbose {
11851178
fmt.Println(srcs.ImportPath)
11861179
}
@@ -1243,8 +1236,8 @@ func (s *Session) ImportResolverFor(srcDir string) func(string) (*compiler.Archi
12431236

12441237
// SourceMappingCallback returns a callback for [github.com/gopherjs/gopherjs/compiler.SourceMapFilter]
12451238
// configured for the current build session.
1246-
func (s *Session) SourceMappingCallback(m *sourcemap.Map) func(generatedLine, generatedColumn int, originalPos token.Position, originalName string) {
1247-
return NewMappingCallback(m, s.xctx.Env().GOROOT, s.xctx.Env().GOPATH, s.options.MapToLocalDisk)
1239+
func (s *Session) EnableMapping(filter *sourcemapx.Filter, jsFileName string) {
1240+
filter.EnableMapping(jsFileName, s.xctx.Env().GOROOT, s.xctx.Env().GOPATH, s.options.MapToLocalDisk)
12481241
}
12491242

12501243
// WriteCommandPackage writes the final JavaScript output file at pkgObj path.
@@ -1260,19 +1253,18 @@ func (s *Session) WriteCommandPackage(archive *compiler.Archive, pkgObj string)
12601253

12611254
sourceMapFilter := &sourcemapx.Filter{Writer: codeFile}
12621255
if s.options.CreateMapFile {
1263-
m := &sourcemap.Map{File: filepath.Base(pkgObj)}
1256+
s.EnableMapping(sourceMapFilter, filepath.Base(pkgObj))
1257+
12641258
mapFile, err := os.Create(pkgObj + ".map")
12651259
if err != nil {
12661260
return err
12671261
}
12681262

12691263
defer func() {
1270-
m.WriteTo(mapFile)
1264+
sourceMapFilter.WriteMappingTo(mapFile)
12711265
mapFile.Close()
12721266
fmt.Fprintf(codeFile, "//# sourceMappingURL=%s.map\n", filepath.Base(pkgObj))
12731267
}()
1274-
1275-
sourceMapFilter.MappingCallback = s.SourceMappingCallback(m)
12761268
}
12771269

12781270
deps, err := compiler.ImportDependencies(archive, s.ImportResolverFor(""))
@@ -1282,50 +1274,6 @@ func (s *Session) WriteCommandPackage(archive *compiler.Archive, pkgObj string)
12821274
return compiler.WriteProgramCode(deps, sourceMapFilter, s.GoRelease())
12831275
}
12841276

1285-
// NewMappingCallback creates a new callback for source map generation.
1286-
func NewMappingCallback(m *sourcemap.Map, goroot, gopath string, localMap bool) func(generatedLine, generatedColumn int, originalPos token.Position, originalName string) {
1287-
return func(generatedLine, generatedColumn int, originalPos token.Position, originalName string) {
1288-
mapping := &sourcemap.Mapping{GeneratedLine: generatedLine, GeneratedColumn: generatedColumn}
1289-
1290-
if originalPos.IsValid() {
1291-
file := originalPos.Filename
1292-
1293-
switch hasGopathPrefix, prefixLen := hasGopathPrefix(file, gopath); {
1294-
case localMap:
1295-
// no-op: keep file as-is
1296-
case hasGopathPrefix:
1297-
file = filepath.ToSlash(file[prefixLen+4:])
1298-
case strings.HasPrefix(file, goroot):
1299-
file = filepath.ToSlash(file[len(goroot)+4:])
1300-
default:
1301-
file = filepath.Base(file)
1302-
}
1303-
mapping.OriginalFile = file
1304-
mapping.OriginalLine = originalPos.Line
1305-
mapping.OriginalColumn = originalPos.Column
1306-
}
1307-
1308-
if originalName != "" {
1309-
mapping.OriginalName = originalName
1310-
}
1311-
1312-
m.AddMapping(mapping)
1313-
}
1314-
}
1315-
1316-
// hasGopathPrefix returns true and the length of the matched GOPATH workspace,
1317-
// iff file has a prefix that matches one of the GOPATH workspaces.
1318-
func hasGopathPrefix(file, gopath string) (hasGopathPrefix bool, prefixLen int) {
1319-
gopathWorkspaces := filepath.SplitList(gopath)
1320-
for _, gopathWorkspace := range gopathWorkspaces {
1321-
gopathWorkspace = filepath.Clean(gopathWorkspace)
1322-
if strings.HasPrefix(file, gopathWorkspace) {
1323-
return true, len(gopathWorkspace)
1324-
}
1325-
}
1326-
return false, 0
1327-
}
1328-
13291277
// WaitForChange watches file system events and returns if either when one of
13301278
// the source files is modified.
13311279
func (s *Session) WaitForChange() {

build/cache/cache.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ type Cacheable interface {
3030

3131
// Cache defines methods to store and load cacheable objects.
3232
type Cache interface {
33-
3433
// Store stores the package with the given import path in the cache.
3534
// Any error inside this method will cause the cache not to be persisted.
3635
//

compiler/compiler.go

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"io"
1313
"strings"
1414

15+
"github.com/gopherjs/gopherjs/compiler/incjs"
1516
"github.com/gopherjs/gopherjs/compiler/internal/dce"
1617
"github.com/gopherjs/gopherjs/compiler/linkname"
1718
"github.com/gopherjs/gopherjs/compiler/prelude"
@@ -65,7 +66,7 @@ type Archive struct {
6566
// Compiled package-level symbols.
6667
Declarations []*Decl
6768
// Concatenated contents of all raw .inc.js of the package.
68-
IncJSCode []byte
69+
IncJSCode []incjs.File
6970
// The file set containing the source code locations for various symbols
7071
// (e.g. for sourcemap generation). See [token.FileSet.Write].
7172
FileSet *token.FileSet
@@ -120,12 +121,6 @@ func ImportDependencies(archive *Archive, importPkg func(string) (*Archive, erro
120121
return deps, nil
121122
}
122123

123-
type dceInfo struct {
124-
decl *Decl
125-
objectFilter string
126-
methodFilter string
127-
}
128-
129124
func WriteProgramCode(pkgs []*Archive, w *sourcemapx.Filter, goVersion string) error {
130125
mainPkg := pkgs[len(pkgs)-1]
131126
minify := mainPkg.Minified
@@ -158,13 +153,10 @@ func WriteProgramCode(pkgs []*Archive, w *sourcemapx.Filter, goVersion string) e
158153
if _, err := writeF(w, false, "var $goVersion = %q;\n", goVersion); err != nil {
159154
return err
160155
}
161-
162-
preludeJS := prelude.Prelude
163-
if minify {
164-
preludeJS = prelude.Minified()
165-
}
166-
if _, err := io.WriteString(w, preludeJS); err != nil {
167-
return err
156+
for _, preludeFile := range prelude.PreludeFiles() {
157+
if _, err := w.WriteJS(preludeFile.Source, preludeFile.Name, minify); err != nil {
158+
return err
159+
}
168160
}
169161
if _, err := writeF(w, false, "\n"); err != nil {
170162
return err
@@ -205,12 +197,22 @@ func WriteProgramCode(pkgs []*Archive, w *sourcemapx.Filter, goVersion string) e
205197
}
206198

207199
func WritePkgCode(pkg *Archive, dceSelection map[*Decl]struct{}, gls linkname.GoLinknameSet, minify bool, w *sourcemapx.Filter) error {
208-
if w.MappingCallback != nil && pkg.FileSet != nil {
200+
if w.IsMapping() && pkg.FileSet != nil {
209201
w.FileSet = pkg.FileSet
210202
}
211-
if _, err := w.Write(pkg.IncJSCode); err != nil {
212-
return err
203+
204+
for _, jsFile := range pkg.IncJSCode {
205+
if _, err := writeF(w, minify, "\t(function() {\n"); err != nil {
206+
return err
207+
}
208+
if _, err := w.WriteJS(string(jsFile.Content), jsFile.Path, minify); err != nil {
209+
return err
210+
}
211+
if _, err := writeF(w, minify, "\n\t}).call($global);\n"); err != nil {
212+
return err
213+
}
213214
}
215+
214216
if _, err := writeF(w, minify, "$packages[\"%s\"] = (function() {\n", pkg.ImportPath); err != nil {
215217
return err
216218
}

compiler/package.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ func Compile(srcs *sources.Sources, tContext *types.Context, minify bool) (_ *Ar
222222
FileSet: srcs.FileSet,
223223
Minified: minify,
224224
GoLinknames: srcs.GoLinknames,
225+
IncJSCode: srcs.JSFiles,
225226
}, nil
226227
}
227228

compiler/prelude/prelude.go

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@ package prelude
22

33
import (
44
_ "embed"
5-
6-
"github.com/evanw/esbuild/pkg/api"
7-
log "github.com/sirupsen/logrus"
5+
"path/filepath"
6+
"runtime/debug"
7+
"strings"
88
)
99

10-
// Prelude is the GopherJS JavaScript interop layer.
11-
var Prelude = prelude + numeric + types + goroutines + jsmapping
12-
1310
//go:embed prelude.js
1411
var prelude string
1512

@@ -25,24 +22,48 @@ var jsmapping string
2522
//go:embed goroutines.js
2623
var goroutines string
2724

28-
func Minified() string {
29-
result := api.Transform(Prelude, api.TransformOptions{
30-
Target: api.ES2015,
31-
MinifyWhitespace: true,
32-
MinifyIdentifiers: true,
33-
MinifySyntax: true,
34-
KeepNames: true,
35-
Charset: api.CharsetUTF8,
36-
LegalComments: api.LegalCommentsEndOfFile,
37-
})
38-
for _, w := range result.Warnings {
39-
log.Warnf("%d:%d: %s\n%s\n", w.Location.Line, w.Location.Column, w.Text, w.Location.LineText)
25+
type PreludeFile struct {
26+
Name string
27+
Source string
28+
}
29+
30+
// PreludeFiles gets the GopherJS JavaScript interop layers.
31+
func PreludeFiles() (files []PreludeFile) {
32+
basePath := getPackagePath()
33+
add := func(name, src string) {
34+
files = append(files, PreludeFile{
35+
Name: filepath.Join(basePath, name),
36+
Source: src,
37+
})
4038
}
41-
if errCount := len(result.Errors); errCount > 0 {
42-
for _, e := range result.Errors {
43-
log.Errorf("%d:%d: %s\n%s\n", e.Location.Line, e.Location.Column, e.Text, e.Location.LineText)
39+
40+
add(`prelude.js`, prelude)
41+
add(`numberic.js`, numeric)
42+
add(`types.js`, types)
43+
add(`goroutines.js`, goroutines)
44+
add(`jsmapping.js`, jsmapping)
45+
return
46+
}
47+
48+
// getPackagePath attempts to determine the package path of the prelude package
49+
// by inspecting the runtime stack. This is used to set the correct paths for
50+
// the prelude files so that source maps work correctly. This should get the
51+
// path that the prelude package is locating when GopherJS is compiled.
52+
func getPackagePath() string {
53+
// Line 0 is goroutine number, line 1 and 2 is runtime/debug.Stack and path,
54+
// line 3 is this function itself, with line 4 being the path for this function.
55+
const pathLineIndex = 4
56+
stack := string(debug.Stack())
57+
lines := strings.Split(stack, "\n")
58+
if len(lines) >= pathLineIndex {
59+
tracePath := strings.TrimSpace(lines[pathLineIndex])
60+
// tracePath should be in the form: "/full/path/to/compiler/prelude/prelude.go:42 +0x123"
61+
// so drop the ending to isolate the folder path.
62+
if index := strings.LastIndex(tracePath, `/`); index >= 0 {
63+
return tracePath[:index]
4464
}
45-
log.Fatalf("Prelude minification failed with %d errors", errCount)
4665
}
47-
return string(result.Code)
66+
// Fallback to a default path. The source maps may not have the correct path
67+
// to open the file in an editor but it will be close enough to be useful.
68+
return `github.com/gopherjs/gopherjs/compiler/prelude/`
4869
}

compiler/sources/serializer.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ func unpackFile(file *ast.File) {
128128
// an interface field in the AST.
129129
var prepareGob = func() func() {
130130
registerTypes := func() {
131-
132131
// Register expression nodes.
133132
gob.Register(&ast.BadExpr{})
134133
gob.Register(&ast.Ident{})

internal/sourcemapx/doc.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Package sourcemapx contains utilities for passing source map information
2-
// around, intended to work with github.com/neelance/sourcemap.
2+
// around, intended to work with a [neelance sourcemap].
3+
//
4+
// # Mapping Go source coude
35
//
46
// GopherJS code generator outputs hints about correspondence between the
57
// generated code and original sources inline. Such hints are marked by the
@@ -13,7 +15,7 @@
1315
// The hinting mechanism is designed to be extensible, the Hint type able to
1416
// wrap different types containing different information:
1517
//
16-
// - go/token.Pos indicates position in the original source the current
18+
// - [go/token.Pos] indicates position in the original source the current
1719
// location in the generated code corresponds to.
1820
// - Identifier maps a JS identifier to the original Go identifier it
1921
// represents.
@@ -23,4 +25,14 @@
2325
// Filter type is used to extract the hints from the written code stream and
2426
// pass them into source map generator. It also ensures that the encoded inline
2527
// hints don't make it into the final output, since they are not valid JS.
28+
//
29+
// # Mapping JS source code
30+
//
31+
// The filter also provides a WriteJS methods that can be used to write pure JS
32+
// code (without hints) through the filter. While it is passing through the filter,
33+
// it will produce a source map for the JS code and pass that source map
34+
// information to a [neelance sourcemap]. This uses [esbuild]
35+
//
36+
// [neelance sourcemap]:github.com/neelance/sourcemap
37+
// [esbuild]: https://esbuild.github.io/
2638
package sourcemapx

0 commit comments

Comments
 (0)