From 934d57fa75979ae4abba7e53968a646e3ff8977b Mon Sep 17 00:00:00 2001 From: Mark Wubben Date: Mon, 21 Mar 2016 16:33:00 +0000 Subject: [PATCH 1/2] use .js.map extension for cached source maps This is the more commonly used extension for source map files. Fix the test which erroneously looked for `.js` files where it meant `.map`. --- lib/caching-precompiler.js | 2 +- lib/test-worker.js | 2 +- test/caching-precompiler.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/caching-precompiler.js b/lib/caching-precompiler.js index 46457a798..930fb530c 100644 --- a/lib/caching-precompiler.js +++ b/lib/caching-precompiler.js @@ -59,7 +59,7 @@ CachingPrecompiler.prototype._factory = function (babelConfig, cacheDir) { code = code.toString(); var options = buildOptions(filename, code); var result = babel.transform(code, options); - var mapFile = path.join(cacheDir, hash + '.map'); + var mapFile = path.join(cacheDir, hash + '.js.map'); fs.writeFileSync(mapFile, JSON.stringify(result.map)); return result.code; }; diff --git a/lib/test-worker.js b/lib/test-worker.js index 388943a41..ac3fe4290 100644 --- a/lib/test-worker.js +++ b/lib/test-worker.js @@ -72,7 +72,7 @@ installPrecompiler(function (filename) { var precompiled = opts.precompiled[filename]; if (precompiled) { - sourceMapCache[filename] = path.join(cacheDir, precompiled + '.map'); + sourceMapCache[filename] = path.join(cacheDir, precompiled + '.js.map'); return fs.readFileSync(path.join(cacheDir, precompiled + '.js'), 'utf8'); } diff --git a/test/caching-precompiler.js b/test/caching-precompiler.js index ea394c899..732606fd8 100644 --- a/test/caching-precompiler.js +++ b/test/caching-precompiler.js @@ -18,7 +18,7 @@ function endsWithJs(filename) { } function endsWithMap(filename) { - return /\.js$/.test(filename); + return /\.js\.map$/.test(filename); } sinon.spy(babel, 'transform'); @@ -50,7 +50,7 @@ test('adds files and source maps to the cache directory as needed', function (t) var files = fs.readdirSync(tempDir); t.is(files.length, 2); t.is(files.filter(endsWithJs).length, 1, 'one .js file is saved to the cache'); - t.is(files.filter(endsWithMap).length, 1, 'one .map file is saved to the cache'); + t.is(files.filter(endsWithMap).length, 1, 'one .js.map file is saved to the cache'); t.end(); }); From 592ff138580e1aca6b7506b94bed7603cd2f39e4 Mon Sep 17 00:00:00 2001 From: Mark Wubben Date: Mon, 21 Mar 2016 17:00:13 +0000 Subject: [PATCH 2/2] ensure source map can be resolved from cached code When loading the test file, test workers intercept the require call and load the cached code instead. Libraries like nyc may also be intercepting require calls, however they won't know that different code was loaded. They may then attempt to resolve a source map from the original file location. This commit adds a source map file comment to the cached code. The file path is relative from the directory of the original file to where the source map is cached. Add a test which mimics how nyc resolves the source map. --- lib/caching-precompiler.js | 16 +++++++++++++++- test/caching-precompiler.js | 29 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lib/caching-precompiler.js b/lib/caching-precompiler.js index 930fb530c..0e7058ab3 100644 --- a/lib/caching-precompiler.js +++ b/lib/caching-precompiler.js @@ -4,6 +4,7 @@ var cachingTransform = require('caching-transform'); var md5Hex = require('md5-hex'); var stripBom = require('strip-bom'); var objectAssign = require('object-assign'); +var generateMapFileComment = require('convert-source-map').generateMapFileComment; module.exports = CachingPrecompiler; @@ -61,7 +62,20 @@ CachingPrecompiler.prototype._factory = function (babelConfig, cacheDir) { var result = babel.transform(code, options); var mapFile = path.join(cacheDir, hash + '.js.map'); fs.writeFileSync(mapFile, JSON.stringify(result.map)); - return result.code; + + // When loading the test file, test workers intercept the require call and + // load the cached code instead. Libraries like nyc may also be intercepting + // require calls, however they won't know that different code was loaded. + // They may then attempt to resolve a source map from the original file + // location. + // + // Add a source map file comment to the cached code. The file path is + // relative from the directory of the original file to where the source map + // is cached. This will allow the source map to be resolved. + var sourceDir = path.dirname(filename); + var relMapFile = path.relative(sourceDir, mapFile); + var mapFileComment = generateMapFileComment(relMapFile); + return result.code + '\n' + mapFileComment; }; }; diff --git a/test/caching-precompiler.js b/test/caching-precompiler.js index 732606fd8..ad0e26314 100644 --- a/test/caching-precompiler.js +++ b/test/caching-precompiler.js @@ -6,6 +6,7 @@ var uniqueTempDir = require('unique-temp-dir'); var sinon = require('sinon'); var babel = require('babel-core'); var transformRuntime = require('babel-plugin-transform-runtime'); +var fromMapFileSource = require('convert-source-map').fromMapFileSource; var CachingPrecompiler = require('../lib/caching-precompiler'); @@ -54,6 +55,34 @@ test('adds files and source maps to the cache directory as needed', function (t) t.end(); }); +test('adds a map file comment to the cached files', function (t) { + var tempDir = uniqueTempDir(); + var precompiler = new CachingPrecompiler(tempDir, null); + + precompiler.precompileFile(fixture('es2015.js')); + + var cachedCode; + var cachedMap; + fs.readdirSync(tempDir).map(function (file) { + return path.join(tempDir, file); + }).forEach(function (file) { + if (endsWithJs(file)) { + cachedCode = fs.readFileSync(file, 'utf8'); + } else if (endsWithMap(file)) { + cachedMap = fs.readFileSync(file, 'utf8'); + } + }); + + // This is comparable to how nyc resolves the source map. It has access to the + // cached code but believes it to come from the original es2015.js fixture. + // Ensure the cached map can be resolved from the cached code. Also see + // . + var foundMap = fromMapFileSource(cachedCode, path.join(__dirname, 'fixture')); + t.ok(foundMap); + t.is(foundMap.toJSON(), cachedMap); + t.end(); +}); + test('uses default babel options when babelConfig === "default"', function (t) { var tempDir = uniqueTempDir(); var precompiler = new CachingPrecompiler(tempDir, 'default');