104 lines
2.9 KiB
JavaScript
104 lines
2.9 KiB
JavaScript
|
'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;
|