mirror of
https://github.com/atom/atom.git
synced 2026-01-24 14:28:14 -05:00
Adding a source map for the entire snapshot was expensive in terms of memory and seemed to be triggering some sort of bug in Chromium when reloading with the DevTools open. The custom row translation relies on a much more compact representation of the data and avoids the crash. Signed-off-by: Nathan Sobo <nathan@github.com>
242 lines
6.9 KiB
JavaScript
242 lines
6.9 KiB
JavaScript
'use strict'
|
|
|
|
// For now, we're not using babel or ES6 features like `let` and `const` in
|
|
// this file, because `apm` requires this file directly in order to pre-warm
|
|
// Atom's compile-cache when installing or updating packages, using an older
|
|
// version of node.js
|
|
|
|
var path = require('path')
|
|
var fs = require('fs-plus')
|
|
var sourceMapSupport = require('@atom/source-map-support')
|
|
|
|
var PackageTranspilationRegistry = require('./package-transpilation-registry')
|
|
var CSON = null
|
|
|
|
var packageTranspilationRegistry = new PackageTranspilationRegistry()
|
|
|
|
var COMPILERS = {
|
|
'.js': packageTranspilationRegistry.wrapTranspiler(require('./babel')),
|
|
'.ts': 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)
|
|
}
|
|
|
|
var cacheStats = {}
|
|
var cacheDirectory = null
|
|
|
|
exports.setAtomHomeDirectory = function (atomHome) {
|
|
var 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)
|
|
var extension = path.extname(filePath)
|
|
|
|
if (extension === '.cson') {
|
|
if (!CSON) {
|
|
CSON = require('season')
|
|
CSON.setCacheDir(this.getCacheDirectory())
|
|
}
|
|
return CSON.readFileSync(filePath)
|
|
} else {
|
|
var 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) {
|
|
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 = compiler.compile(sourceCode, 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 cachePath = path.join(cacheDirectory, relativeCachePath)
|
|
fs.writeFileSync(cachePath, code, 'utf8')
|
|
}
|
|
|
|
var INLINE_SOURCE_MAP_REGEXP = /\/\/[#@]\s*sourceMappingURL=([^'"\n]+)\s*$/mg
|
|
|
|
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
|
|
}
|
|
|
|
var 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
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
})
|
|
|
|
var 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
|
|
})
|
|
}
|
|
})
|
|
|
|
Error.prototype.getRawStack = function () { // eslint-disable-line no-extend-native
|
|
// Access this.stack to ensure prepareStackTrace has been run on this error
|
|
// because it assigns this.rawStack as a side-effect
|
|
this.stack
|
|
return this.rawStack
|
|
}
|
|
|
|
Object.keys(COMPILERS).forEach(function (extension) {
|
|
var compiler = COMPILERS[extension]
|
|
|
|
Object.defineProperty(nodeRequire.extensions, extension, {
|
|
enumerable: true,
|
|
writable: false,
|
|
value: function (module, filePath) {
|
|
var code = compileFileAtPath(compiler, filePath, extension)
|
|
return module._compile(code, filePath)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
exports.supportedExtensions = Object.keys(COMPILERS)
|
|
exports.resetCacheStats()
|