mirror of
https://github.com/atom/atom.git
synced 2026-04-28 03:01:47 -04:00
260 lines
7.1 KiB
JavaScript
260 lines
7.1 KiB
JavaScript
'use strict';
|
|
|
|
// Atom's compile-cache when installing or updating packages, called by apm's Node-js
|
|
|
|
const path = require('path');
|
|
const fs = require('fs-plus');
|
|
const sourceMapSupport = require('@atom/source-map-support');
|
|
|
|
const PackageTranspilationRegistry = require('./package-transpilation-registry');
|
|
let CSON = null;
|
|
|
|
const packageTranspilationRegistry = new PackageTranspilationRegistry();
|
|
|
|
const COMPILERS = {
|
|
'.js': packageTranspilationRegistry.wrapTranspiler(require('./babel')),
|
|
'.ts': packageTranspilationRegistry.wrapTranspiler(require('./typescript')),
|
|
'.tsx': packageTranspilationRegistry.wrapTranspiler(require('./typescript')),
|
|
'.coffee': packageTranspilationRegistry.wrapTranspiler(
|
|
require('./coffee-script')
|
|
)
|
|
};
|
|
|
|
exports.addTranspilerConfigForPath = function(
|
|
packagePath,
|
|
packageName,
|
|
packageMeta,
|
|
config
|
|
) {
|
|
packagePath = fs.realpathSync(packagePath);
|
|
packageTranspilationRegistry.addTranspilerConfigForPath(
|
|
packagePath,
|
|
packageName,
|
|
packageMeta,
|
|
config
|
|
);
|
|
};
|
|
|
|
exports.removeTranspilerConfigForPath = function(packagePath) {
|
|
packagePath = fs.realpathSync(packagePath);
|
|
packageTranspilationRegistry.removeTranspilerConfigForPath(packagePath);
|
|
};
|
|
|
|
const cacheStats = {};
|
|
let cacheDirectory = null;
|
|
|
|
exports.setAtomHomeDirectory = function(atomHome) {
|
|
let cacheDir = path.join(atomHome, '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.setCacheDirectory = function(directory) {
|
|
cacheDirectory = directory;
|
|
};
|
|
|
|
exports.getCacheDirectory = function() {
|
|
return cacheDirectory;
|
|
};
|
|
|
|
exports.addPathToCache = function(filePath, atomHome) {
|
|
this.setAtomHomeDirectory(atomHome);
|
|
const extension = path.extname(filePath);
|
|
|
|
if (extension === '.cson') {
|
|
if (!CSON) {
|
|
CSON = require('season');
|
|
CSON.setCacheDir(this.getCacheDirectory());
|
|
}
|
|
return CSON.readFileSync(filePath);
|
|
} else {
|
|
const compiler = COMPILERS[extension];
|
|
if (compiler) {
|
|
return 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) {
|
|
const sourceCode = fs.readFileSync(filePath, 'utf8');
|
|
if (compiler.shouldCompile(sourceCode, filePath)) {
|
|
const cachePath = compiler.getCachePath(sourceCode, filePath);
|
|
let compiledCode = readCachedJavaScript(cachePath);
|
|
if (compiledCode != null) {
|
|
cacheStats[extension].hits++;
|
|
} else {
|
|
cacheStats[extension].misses++;
|
|
compiledCode = compiler.compile(sourceCode, filePath);
|
|
writeCachedJavaScript(cachePath, compiledCode);
|
|
}
|
|
return compiledCode;
|
|
}
|
|
return sourceCode;
|
|
}
|
|
|
|
function readCachedJavaScript(relativeCachePath) {
|
|
const cachePath = path.join(cacheDirectory, relativeCachePath);
|
|
if (fs.isFileSync(cachePath)) {
|
|
try {
|
|
return fs.readFileSync(cachePath, 'utf8');
|
|
} catch (error) {}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function writeCachedJavaScript(relativeCachePath, code) {
|
|
const cachePath = path.join(cacheDirectory, relativeCachePath);
|
|
fs.writeFileSync(cachePath, code, 'utf8');
|
|
}
|
|
|
|
const INLINE_SOURCE_MAP_REGEXP = /\/\/[#@]\s*sourceMappingURL=([^'"\n]+)\s*$/gm;
|
|
|
|
exports.install = function(resourcesPath, nodeRequire) {
|
|
const snapshotSourceMapConsumer = {
|
|
originalPositionFor({ line, column }) {
|
|
const { relativePath, row } = snapshotResult.translateSnapshotRow(line);
|
|
return {
|
|
column,
|
|
line: row,
|
|
source: path.join(resourcesPath, 'app', 'static', relativePath),
|
|
name: null
|
|
};
|
|
}
|
|
};
|
|
|
|
sourceMapSupport.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 (filePath === '<embedded>') {
|
|
return { map: snapshotSourceMapConsumer };
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
let compiler = COMPILERS[path.extname(filePath)];
|
|
if (!compiler) compiler = COMPILERS['.js'];
|
|
|
|
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;
|
|
}
|
|
|
|
let match, lastMatch;
|
|
INLINE_SOURCE_MAP_REGEXP.lastIndex = 0;
|
|
while ((match = INLINE_SOURCE_MAP_REGEXP.exec(fileData))) {
|
|
lastMatch = match;
|
|
}
|
|
if (lastMatch == null) {
|
|
return null;
|
|
}
|
|
|
|
const sourceMappingURL = lastMatch[1];
|
|
const rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(',') + 1);
|
|
|
|
try {
|
|
var sourceMap = JSON.parse(Buffer.from(rawData, 'base64'));
|
|
} catch (error) {
|
|
console.warn('Error parsing source map', error.stack);
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
map: sourceMap,
|
|
url: null
|
|
};
|
|
}
|
|
});
|
|
|
|
const prepareStackTraceWithSourceMapping = Error.prepareStackTrace;
|
|
var prepareStackTrace = prepareStackTraceWithSourceMapping;
|
|
|
|
function prepareStackTraceWithRawStackAssignment(error, frames) {
|
|
if (error.rawStack) {
|
|
// avoid infinite recursion
|
|
return prepareStackTraceWithSourceMapping(error, frames);
|
|
} else {
|
|
error.rawStack = frames;
|
|
return prepareStackTrace(error, frames);
|
|
}
|
|
}
|
|
|
|
Error.stackTraceLimit = 30;
|
|
|
|
Object.defineProperty(Error, 'prepareStackTrace', {
|
|
get: function() {
|
|
return prepareStackTraceWithRawStackAssignment;
|
|
},
|
|
|
|
set: function(newValue) {
|
|
prepareStackTrace = newValue;
|
|
process.nextTick(function() {
|
|
prepareStackTrace = prepareStackTraceWithSourceMapping;
|
|
});
|
|
}
|
|
});
|
|
|
|
// eslint-disable-next-line no-extend-native
|
|
Error.prototype.getRawStack = function() {
|
|
// Access this.stack to ensure prepareStackTrace has been run on this error
|
|
// because it assigns this.rawStack as a side-effect
|
|
this.stack; // eslint-disable-line no-unused-expressions
|
|
return this.rawStack;
|
|
};
|
|
|
|
Object.keys(COMPILERS).forEach(function(extension) {
|
|
const compiler = COMPILERS[extension];
|
|
|
|
Object.defineProperty(nodeRequire.extensions, extension, {
|
|
enumerable: true,
|
|
writable: false,
|
|
value: function(module, filePath) {
|
|
const code = compileFileAtPath(compiler, filePath, extension);
|
|
return module._compile(code, filePath);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
exports.supportedExtensions = Object.keys(COMPILERS);
|
|
exports.resetCacheStats();
|