From 43bb38420d4c3ad7012f890f5cbc792002ec4027 Mon Sep 17 00:00:00 2001 From: Greg Magolan Date: Wed, 25 Sep 2019 16:08:31 -0700 Subject: [PATCH] feat(terser): support source map files --- packages/terser/src/terser_minified.bzl | 22 +++++++++++----- packages/terser/test/sourcemap/BUILD.bazel | 26 +++++++++++++++++++ packages/terser/test/sourcemap/README.md | 1 + packages/terser/test/sourcemap/src1.js | 10 +++++++ packages/terser/test/sourcemap/src1.js.map | 1 + packages/terser/test/sourcemap/src1.ts | 7 +++++ packages/terser/test/sourcemap/terser_spec.js | 21 +++++++++++++++ 7 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 packages/terser/test/sourcemap/BUILD.bazel create mode 100644 packages/terser/test/sourcemap/README.md create mode 100644 packages/terser/test/sourcemap/src1.js create mode 100644 packages/terser/test/sourcemap/src1.js.map create mode 100644 packages/terser/test/sourcemap/src1.ts create mode 100644 packages/terser/test/sourcemap/terser_spec.js diff --git a/packages/terser/src/terser_minified.bzl b/packages/terser/src/terser_minified.bzl index a0fffec9d7..88919cd840 100644 --- a/packages/terser/src/terser_minified.bzl +++ b/packages/terser/src/terser_minified.bzl @@ -40,7 +40,7 @@ Can be a .js file, a rule producing .js files as its default output, or a rule p Note that you can pass multiple files to terser, which it will bundle together. If you want to do this, you can pass a filegroup here.""", - allow_files = [".js"], + allow_files = [".js", ".map", ".mjs"], mandatory = True, ), "config_file": attr.label( @@ -95,6 +95,9 @@ so that it only affects the current build. ), } +def _filter_js(files): + return [f for f in files if f.is_directory or f.extension == "js" or f.extension == "mjs"] + def _terser(ctx): "Generate actions to create terser config run terser" @@ -103,9 +106,11 @@ def _terser(ctx): inputs = ctx.files.src[:] outputs = [] - directory_srcs = [s for s in ctx.files.src if s.is_directory] + sources = _filter_js(inputs) + sourcemaps = [f for f in inputs if f.extension == "map"] + directory_srcs = [s for s in sources if s.is_directory] if len(directory_srcs) > 0: - if len(ctx.files.src) > 1: + if len(sources) > 1: fail("When directories are passed to terser_minified, there should be only one input") outputs.append(ctx.actions.declare_directory(ctx.label.name)) else: @@ -113,7 +118,7 @@ def _terser(ctx): if ctx.attr.sourcemap: outputs.append(ctx.actions.declare_file("%s.js.map" % ctx.label.name)) - args.add_all([s.path for s in ctx.files.src]) + args.add_all([s.path for s in sources]) args.add_all(["--output", outputs[0].path]) debug = ctx.attr.debug or "DEBUG" in ctx.var.keys() @@ -126,9 +131,12 @@ def _terser(ctx): # see https://github.com/terser-js/terser#command-line-usage source_map_opts = ["includeSources", "base=" + ctx.bin_dir.path] - # We support only inline sourcemaps for now. - # It's hard to pair up the .js inputs with corresponding .map files - source_map_opts.append("content=inline") + if len(sourcemaps) == 0: + source_map_opts.append("content=inline") + elif len(sourcemaps) == 1: + source_map_opts.append("content='%s'" % sourcemaps[0].path) + else: + fail("When sourcemap is True, there should only be one or none input sourcemaps") # This option doesn't work in the config file, only on the CLI args.add_all(["--source-map", ",".join(source_map_opts)]) diff --git a/packages/terser/test/sourcemap/BUILD.bazel b/packages/terser/test/sourcemap/BUILD.bazel new file mode 100644 index 0000000000..b8bc551b89 --- /dev/null +++ b/packages/terser/test/sourcemap/BUILD.bazel @@ -0,0 +1,26 @@ +load("@npm_bazel_jasmine//:index.from_src.bzl", "jasmine_node_test") +load("@npm_bazel_terser//:index.from_src.bzl", "terser_minified") + +filegroup( + name = "src1_and_map", + srcs = [ + "src1.js", + "src1.js.map", + ], +) + +terser_minified( + name = "src1.min", + src = ":src1_and_map", +) + +jasmine_node_test( + name = "test", + srcs = [ + "terser_spec.js", + ], + data = ["@npm//source-map"], + deps = [ + ":src1.min", + ], +) diff --git a/packages/terser/test/sourcemap/README.md b/packages/terser/test/sourcemap/README.md new file mode 100644 index 0000000000..3f25b35417 --- /dev/null +++ b/packages/terser/test/sourcemap/README.md @@ -0,0 +1 @@ +The src1.js{,.map} files were generated from running `tsc --lib es2015,dom --sourcemap --noImplicitUseStrict src1.ts`. diff --git a/packages/terser/test/sourcemap/src1.js b/packages/terser/test/sourcemap/src1.js new file mode 100644 index 0000000000..ad3902e964 --- /dev/null +++ b/packages/terser/test/sourcemap/src1.js @@ -0,0 +1,10 @@ +exports.__esModule = true; +// clang-format off +/*a comment*/ var MyClass = /** @class */ (function () { + function MyClass(s) { + console.log(s); + } + return MyClass; +}()); +exports.MyClass = MyClass; +//# sourceMappingURL=src1.js.map \ No newline at end of file diff --git a/packages/terser/test/sourcemap/src1.js.map b/packages/terser/test/sourcemap/src1.js.map new file mode 100644 index 0000000000..8d7b122ca0 --- /dev/null +++ b/packages/terser/test/sourcemap/src1.js.map @@ -0,0 +1 @@ +{"version":3,"file":"src1.js","sourceRoot":"","sources":["src1.ts"],"names":[],"mappings":";AAAA,mBAAmB;AACnB,aAAa,CAAC;IACZ,iBAAY,CAAS;QACnB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAEH,cAAC;AAAD,CAAC,AALa,IAKb;AAL0B,0BAAO"} \ No newline at end of file diff --git a/packages/terser/test/sourcemap/src1.ts b/packages/terser/test/sourcemap/src1.ts new file mode 100644 index 0000000000..f60f2b78bd --- /dev/null +++ b/packages/terser/test/sourcemap/src1.ts @@ -0,0 +1,7 @@ +// clang-format off +/*a comment*/ export class MyClass { + constructor(s: string) { + console.log(s); + } + field: string|undefined; +} diff --git a/packages/terser/test/sourcemap/terser_spec.js b/packages/terser/test/sourcemap/terser_spec.js new file mode 100644 index 0000000000..2022390787 --- /dev/null +++ b/packages/terser/test/sourcemap/terser_spec.js @@ -0,0 +1,21 @@ +const fs = require('fs'); +const sm = require('source-map'); +const DIR = 'build_bazel_rules_nodejs/packages/terser/test/sourcemap'; + +describe('terser sourcemap handling', () => { + it('should produce a sourcemap output', async () => { + const file = require.resolve(DIR + '/src1.min.js.map'); + const rawSourceMap = JSON.parse(fs.readFileSync(file, 'utf-8')); + await sm.SourceMapConsumer.with(rawSourceMap, null, consumer => { + const pos = consumer.originalPositionFor({ + // src1.min.js has MyClass at column 18 + line: 1, + column: 18 + }); + expect(pos.source).toBe('src1.ts'); + expect(pos.line).toBe(2); + expect(pos.column).toBe(14); + expect(pos.name).toBe('MyClass'); + }); + }); +}); \ No newline at end of file