diff --git a/lib/caching-precompiler.js b/lib/caching-precompiler.js index 46457a798..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; @@ -59,9 +60,22 @@ 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; + + // 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/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..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'); @@ -18,7 +19,7 @@ function endsWithJs(filename) { } function endsWithMap(filename) { - return /\.js$/.test(filename); + return /\.js\.map$/.test(filename); } sinon.spy(babel, 'transform'); @@ -50,7 +51,35 @@ 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(); +}); + +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(); });