You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

103 lines
2.9 KiB

'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;