Mailspring/src/compile-cache.js

191 lines
5 KiB
JavaScript

'use strict'
var path = require('path')
var fs = require('fs-plus')
var babelCompiler = require('./compile-support/babel')
var coffeeCompiler = require('./compile-support/coffee-script')
var typescriptCompiler = require('./compile-support/typescript')
var CSON = null
var COMPILERS = {
'.js': babelCompiler,
'.jsx': babelCompiler,
'.es6': babelCompiler,
'.ts': typescriptCompiler,
'.coffee': coffeeCompiler,
'.cjsx': coffeeCompiler
}
var cacheStats = {}
var cacheDirectory = null
exports.setHomeDirectory = function (nylasHome) {
var cacheDir = path.join(nylasHome, 'compile-cache')
if (process.env.USER === 'root' && process.env.SUDO_USER && process.env.SUDO_USER !== process.env.USER) {
cacheDir = path.join(cacheDir, 'root')
}
this.setCacheDirectory(cacheDir)
}
exports.setHotReload = function(hotreload) {
// This sets require.extensions['.cjsx']
if (hotreload) {
require('./compile-support/cjsx').register();
}
}
exports.setCacheDirectory = function (directory) {
cacheDirectory = directory
}
exports.getCacheDirectory = function () {
return cacheDirectory
}
exports.addPathToCache = function (filePath, nylasHome) {
this.setHomeDirectory(nylasHome)
var extension = path.extname(filePath)
if (extension === '.cson') {
if (!CSON) {
CSON = require('season')
CSON.setCacheDir(this.getCacheDirectory())
}
CSON.readFileSync(filePath)
} else {
var compiler = COMPILERS[extension]
if (compiler) {
compileFileAtPath(compiler, filePath, extension)
}
}
}
exports.getCacheStats = function () {
return cacheStats
}
exports.resetCacheStats = function () {
Object.keys(COMPILERS).forEach(function (extension) {
cacheStats[extension] = {
hits: 0,
misses: 0
}
})
}
function compileFileAtPath (compiler, filePath, extension, module) {
var sourceCode = fs.readFileSync(filePath, 'utf8')
if (compiler.shouldCompile(sourceCode, filePath)) {
var cachePath = compiler.getCachePath(sourceCode, filePath)
var compiledCode = readCachedJavascript(cachePath)
if (compiledCode != null) {
cacheStats[extension].hits++
} else {
cacheStats[extension].misses++
compiledCode = addSourceURL(compiler.compile(sourceCode, filePath), filePath)
writeCachedJavascript(cachePath, compiledCode)
}
return compiledCode
}
return sourceCode
}
function readCachedJavascript (relativeCachePath) {
var cachePath = path.join(cacheDirectory, relativeCachePath)
if (fs.isFileSync(cachePath)) {
try {
return fs.readFileSync(cachePath, 'utf8')
} catch (error) {}
}
return null
}
function writeCachedJavascript (relativeCachePath, code) {
var cacheTmpPath = path.join(cacheDirectory, relativeCachePath + '.' + process.pid)
var cachePath = path.join(cacheDirectory, relativeCachePath)
fs.writeFileSync(cacheTmpPath, code, 'utf8')
fs.renameSync(cacheTmpPath, cachePath)
}
function addSourceURL (jsCode, filePath) {
if (process.platform === 'win32') {
filePath = '/' + path.resolve(filePath).replace(/\\/g, '/')
}
return jsCode + '\n' + '//# sourceURL=' + encodeURI(filePath) + '\n'
}
var INLINE_SOURCE_MAP_REGEXP = /\/\/[#@]\s*sourceMappingURL=([^'"\n]+)\s*$/mg
require('source-map-support').install({
handleUncaughtExceptions: false,
// Most of this logic is the same as the default implementation in the
// source-map-support module, but we've overridden it to read the javascript
// code from our cache directory.
retrieveSourceMap: function (filePath) {
if (!cacheDirectory || !fs.isFileSync(filePath)) {
return null
}
try {
var sourceCode = fs.readFileSync(filePath, 'utf8')
} catch (error) {
console.warn('Error reading source file', error.stack)
return null
}
var compiler = COMPILERS[path.extname(filePath)]
try {
var fileData = readCachedJavascript(compiler.getCachePath(sourceCode, filePath))
} catch (error) {
console.warn('Error reading compiled file', error.stack)
return null
}
if (fileData == null) {
return null
}
var match, lastMatch
INLINE_SOURCE_MAP_REGEXP.lastIndex = 0
while ((match = INLINE_SOURCE_MAP_REGEXP.exec(fileData))) {
lastMatch = match
}
if (lastMatch == null) {
return null
}
var sourceMappingURL = lastMatch[1]
var rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(',') + 1)
try {
var sourceMap = JSON.parse(new Buffer(rawData, 'base64'))
} catch (error) {
console.warn('Error parsing source map', error.stack)
return null
}
return {
map: sourceMap,
url: null
}
}
})
Object.keys(COMPILERS).forEach(function (extension) {
var compiler = COMPILERS[extension]
Object.defineProperty(require.extensions, extension, {
enumerable: true,
writable: true,
value: function (module, filePath) {
var code = compileFileAtPath(compiler, filePath, extension)
return module._compile(code, filePath)
}
})
})
exports.resetCacheStats()