'use strict'; const path = require('path'); const fs = require('fs'); const convertSourceMap = require('convert-source-map'); const cachingTransform = require('caching-transform'); const packageHash = require('package-hash'); const stripBomBuf = require('strip-bom-buf'); const autoBind = require('auto-bind'); const md5Hex = require('md5-hex'); function getSourceMap(filePath, code) { let sourceMap = convertSourceMap.fromSource(code); if (!sourceMap) { const dirPath = path.dirname(filePath); sourceMap = convertSourceMap.fromMapFileSource(code, dirPath); } if (sourceMap) { sourceMap = sourceMap.toObject(); } return sourceMap; } class CachingPrecompiler { constructor(options) { autoBind(this); this.getBabelOptions = options.getBabelOptions; this.babelCacheKeys = options.babelCacheKeys; this.cacheDirPath = options.path; this.fileHashes = {}; this.transform = this._createTransform(); } precompileFile(filePath) { if (!this.fileHashes[filePath]) { const source = stripBomBuf(fs.readFileSync(filePath)); this.transform(source, filePath); } return this.fileHashes[filePath]; } // Conditionally called by caching-transform when precompiling is required _init() { this.babel = require('babel-core'); return this._transform; } _transform(code, filePath, hash) { code = code.toString(); let result; const originalBabelDisableCache = process.env.BABEL_DISABLE_CACHE; try { // Disable Babel's cache. AVA has good cache management already. process.env.BABEL_DISABLE_CACHE = '1'; result = this.babel.transform(code, Object.assign(this.getBabelOptions(), { inputSourceMap: getSourceMap(filePath, code), filename: filePath, sourceMaps: true, ast: false })); } finally { // Restore the original value. It is passed to workers, where users may // not want Babel's cache to be disabled. process.env.BABEL_DISABLE_CACHE = originalBabelDisableCache; } // Save source map const mapPath = path.join(this.cacheDirPath, `${hash}.js.map`); fs.writeFileSync(mapPath, JSON.stringify(result.map)); // Append source map comment to transformed code // So that other libraries (like nyc) can find the source map const dirPath = path.dirname(filePath); const relativeMapPath = path.relative(dirPath, mapPath); const comment = convertSourceMap.generateMapFileComment(relativeMapPath); return `${result.code}\n${comment}`; } _createTransform() { const salt = packageHash.sync([ require.resolve('../package.json'), require.resolve('babel-core/package.json') ], this.babelCacheKeys); return cachingTransform({ factory: this._init, cacheDir: this.cacheDirPath, hash: this._generateHash, salt, ext: '.js' }); } _generateHash(code, filePath, salt) { const hash = md5Hex([code, filePath, salt]); this.fileHashes[filePath] = hash; return hash; } } module.exports = CachingPrecompiler;