diff --git a/parser/parser.go b/parser/parser.go index 24b380249..a1795271b 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -53,6 +53,7 @@ const ( type options struct { disableSourceMaps bool + skipEmptySourceMaps bool sourceMapLoader func(path string) ([]byte, error) } @@ -66,6 +67,13 @@ func WithDisableSourceMaps(opts *options) { opts.disableSourceMaps = true } +// WithSkipEmptySourceMaps is an option to ignore source maps that are empty rather than fail parsing. +// This is particulary useful for code compiled from TypeScript. The TypeScript compiler outputs empty +// source maps for files containing only type definitions. +func WithSkipEmptySourceMaps(opts *options) { + opts.skipEmptySourceMaps = true +} + // WithSourceMapLoader is an option to set a custom source map loader. The loader will be given a path or a // URL from the sourceMappingURL. If sourceMappingURL is not absolute it is resolved relatively to the name // of the file being parsed. Any error returned by the loader will fail the parsing. diff --git a/parser/parser_test.go b/parser/parser_test.go index e09a7d6cc..8e2b18f3b 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -1235,6 +1235,25 @@ var x = {}; is(err, nil) is(count, 1) is(requestedPath, "https://site.com/delme.js.map") + + // Checks related to empty source maps. + emptySourceMapLoader := func(p string) ([]byte, error) { + count++ + requestedPath = p + emptySourceMap := `{"version":3,"file":"delme.js","sourceRoot":"","sources":["../src/delme.ts"],"names":[],"mappings":""}` + return []byte(emptySourceMap), nil + } + + // First, ensure that the expected error is produced for an empty source map. + _, err = ParseFile(nil, "delme.js", src, 0, WithSourceMapLoader(emptySourceMapLoader)) + is(true, strings.Contains(err.Error(), errSourceMapEmptyString)) + + // Then, ensure WithSkipEmptySourceMaps bypasses the error. + count = 0 + _, err = ParseFile(nil, "delme.js", src, 0, WithSourceMapLoader(emptySourceMapLoader), WithSkipEmptySourceMaps) + is(err, nil) + is(count, 1) + is(requestedPath, "delme.js.map") }) } diff --git a/parser/statement.go b/parser/statement.go index 8ec5cdeb7..e76f5c4a9 100644 --- a/parser/statement.go +++ b/parser/statement.go @@ -12,6 +12,11 @@ import ( "github.com/go-sourcemap/sourcemap" ) +const ( + // Copied from https://github.com/go-sourcemap/sourcemap/blob/794171861aeb0f83fcd66eeb0415a5062594cde6/mappings.go#L35 + errSourceMapEmptyString = "sourcemap: mappings are empty" +) + func (self *_parser) parseBlockStatement() *ast.BlockStatement { node := &ast.BlockStatement{} node.LeftBrace = self.expect(token.LEFT_BRACE) @@ -950,6 +955,8 @@ func (self *_parser) parseSourceMap() *sourcemap.Consumer { if sm, err := sourcemap.Parse(self.file.Name(), data); err == nil { return sm + } else if self.opts.skipEmptySourceMaps && strings.Contains(err.Error(), errSourceMapEmptyString) { + return nil } else { self.error(file.Idx(0), "Could not parse source map: %v", err) }