diff --git a/package.json b/package.json index 84ca82304..a70ee36c1 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,9 @@ "atom-keymap": "^3.1.2", "bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372", "clear-cut": "0.4.0", + "coffee-cash": "0.7.0", "coffee-script": "1.8.0", - "coffeestack": "0.8.0", + "coffeestack": "^1.1", "color": "^0.7.3", "delegato": "^1", "emissary": "^1.3.1", @@ -35,10 +36,10 @@ "fstream": "0.1.24", "fuzzaldrin": "^2.1", "git-utils": "^3.0.0", - "grim": "1.1.1", + "grim": "1.1.2", "guid": "0.0.10", "jasmine-json": "~0.0", - "jasmine-tagged": "^1.1.3", + "jasmine-tagged": "^1.1.4", "jquery": "^2.1.1", "less-cache": "0.21", "marked": "^0.3", @@ -57,7 +58,7 @@ "scandal": "2.0.0", "scoped-property-store": "^0.16.2", "scrollbar-style": "^2.0.0", - "season": "^5.1.2", + "season": "^5.1.4", "semver": "~4.2", "serializable": "^1", "service-hub": "^0.4.0", @@ -93,7 +94,7 @@ "deprecation-cop": "0.36.0", "dev-live-reload": "0.41.0", "encoding-selector": "0.18.0", - "exception-reporting": "0.23.0", + "exception-reporting": "0.24.0", "find-and-replace": "0.157.0", "fuzzy-finder": "0.66.0", "git-diff": "0.52.0", diff --git a/spec/compile-cache-spec.coffee b/spec/compile-cache-spec.coffee new file mode 100644 index 000000000..0cae70434 --- /dev/null +++ b/spec/compile-cache-spec.coffee @@ -0,0 +1,28 @@ +path = require 'path' +CSON = require 'season' +CoffeeCache = require 'coffee-cash' + +to5 = require '../src/6to5' +CompileCache = require '../src/compile-cache' + +describe "Compile Cache", -> + describe ".addPathToCache(filePath)", -> + it "adds the path to the correct CSON, CoffeeScript, or 6to5 cache", -> + spyOn(CSON, 'readFileSync').andCallThrough() + spyOn(CoffeeCache, 'addPathToCache').andCallThrough() + spyOn(to5, 'addPathToCache').andCallThrough() + + CompileCache.addPathToCache(path.join(__dirname, 'fixtures', 'cson.cson')) + expect(CSON.readFileSync.callCount).toBe 1 + expect(CoffeeCache.addPathToCache.callCount).toBe 0 + expect(to5.addPathToCache.callCount).toBe 0 + + CompileCache.addPathToCache(path.join(__dirname, 'fixtures', 'coffee.coffee')) + expect(CSON.readFileSync.callCount).toBe 1 + expect(CoffeeCache.addPathToCache.callCount).toBe 1 + expect(to5.addPathToCache.callCount).toBe 0 + + CompileCache.addPathToCache(path.join(__dirname, 'fixtures', '6to5', 'double-quotes.js')) + expect(CSON.readFileSync.callCount).toBe 1 + expect(CoffeeCache.addPathToCache.callCount).toBe 1 + expect(to5.addPathToCache.callCount).toBe 1 diff --git a/spec/fixtures/cson.cson b/spec/fixtures/cson.cson new file mode 100644 index 000000000..6fe1e8a01 --- /dev/null +++ b/spec/fixtures/cson.cson @@ -0,0 +1 @@ +a: 4 diff --git a/src/6to5.coffee b/src/6to5.coffee index 15059ad62..9d67efcf8 100644 --- a/src/6to5.coffee +++ b/src/6to5.coffee @@ -89,6 +89,7 @@ create6to5VersionAndOptionsDigest = (version, options) -> updateDigestForJsonValue(shasum, options) shasum.digest('hex') +cacheDir = null jsCacheDir = null getCachePath = (sourceCode) -> @@ -96,8 +97,7 @@ getCachePath = (sourceCode) -> unless jsCacheDir? to5Version = require('6to5-core/package.json').version - cacheDir = path.join(process.env.ATOM_HOME, 'compile-cache') - jsCacheDir = path.join(cacheDir, 'js', '6to5', create6to5VersionAndOptionsDigest(to5Version, defaultOptions)) + jsCacheDir = path.join(cacheDir, create6to5VersionAndOptionsDigest(to5Version, defaultOptions)) path.join(jsCacheDir, "#{digest}.js") @@ -141,12 +141,19 @@ loadFile = (module, filePath) -> register = -> Object.defineProperty(require.extensions, '.js', { + enumerable: true writable: false value: loadFile }) +setCacheDirectory = (newCacheDir) -> + if cacheDir isnt newCacheDir + cacheDir = newCacheDir + jsCacheDir = null + module.exports = register: register + setCacheDirectory: setCacheDirectory getCacheMisses: -> stats.misses getCacheHits: -> stats.hits diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 22360a076..3b6018d5b 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -15,6 +15,8 @@ process.on 'uncaughtException', (error={}) -> start = -> setupAtomHome() + setupCoffeeCache() + if process.platform is 'win32' SquirrelUpdate = require './squirrel-update' squirrelCommand = process.argv[1] @@ -49,9 +51,7 @@ start = -> else path.resolve(pathToOpen) - setupCoffeeScript() if args.devMode - require(path.join(args.resourcePath, 'src', 'coffee-cache')).register() AtomApplication = require path.join(args.resourcePath, 'src', 'browser', 'atom-application') else AtomApplication = require './atom-application' @@ -66,15 +66,6 @@ global.devResourcePath = path.normalize(global.devResourcePath) if global.devRes setupCrashReporter = -> crashReporter.start(productName: 'Atom', companyName: 'GitHub') -setupCoffeeScript = -> - CoffeeScript = null - - require.extensions['.coffee'] = (module, filePath) -> - CoffeeScript ?= require('coffee-script') - coffee = fs.readFileSync(filePath, 'utf8') - js = CoffeeScript.compile(coffee, filename: filePath) - module._compile(js, filePath) - setupAtomHome = -> return if process.env.ATOM_HOME @@ -83,6 +74,15 @@ setupAtomHome = -> atomHome = fs.realpathSync(atomHome) process.env.ATOM_HOME = atomHome +setupCoffeeCache = -> + CoffeeCache = require 'coffee-cash' + cacheDir = path.join(process.env.ATOM_HOME, 'compile-cache') + # Use separate compile cache when sudo'ing as root to avoid permission issues + if process.env.USER is 'root' and process.env.SUDO_USER and process.env.SUDO_USER isnt process.env.USER + cacheDir = path.join(cacheDir, 'root') + CoffeeCache.setCacheDirectory(path.join(cacheDir, 'coffee')) + CoffeeCache.register() + parseCommandLine = -> version = app.getVersion() options = optimist(process.argv[1..]) diff --git a/src/coffee-cache.coffee b/src/coffee-cache.coffee deleted file mode 100644 index 1e06fd4d5..000000000 --- a/src/coffee-cache.coffee +++ /dev/null @@ -1,76 +0,0 @@ -crypto = require 'crypto' -path = require 'path' - -CoffeeScript = require 'coffee-script' -CSON = require 'season' -fs = require 'fs-plus' - -cacheDir = path.join(process.env.ATOM_HOME, 'compile-cache') - -stats = - hits: 0 - misses: 0 - -# Use separate compile cache when sudo'ing as root to avoid permission issues -if process.env.USER is 'root' and process.env.SUDO_USER and process.env.SUDO_USER isnt process.env.USER - cacheDir = path.join(cacheDir, 'root') - -coffeeCacheDir = path.join(cacheDir, 'coffee') -CSON.setCacheDir(path.join(cacheDir, 'cson')) - -getCachePath = (coffee) -> - digest = crypto.createHash('sha1').update(coffee, 'utf8').digest('hex') - path.join(coffeeCacheDir, "#{digest}.js") - -getCachedJavaScript = (cachePath) -> - if fs.isFileSync(cachePath) - try - cachedJavaScript = fs.readFileSync(cachePath, 'utf8') - stats.hits++ - return cachedJavaScript - return - -convertFilePath = (filePath) -> - if process.platform is 'win32' - filePath = "/#{path.resolve(filePath).replace(/\\/g, '/')}" - encodeURI(filePath) - -compileCoffeeScript = (coffee, filePath, cachePath) -> - {js, v3SourceMap} = CoffeeScript.compile(coffee, filename: filePath, sourceMap: true) - stats.misses++ - # Include source map in the web page environment. - if btoa? and JSON? and unescape? and encodeURIComponent? - js = "#{js}\n//# sourceMappingURL=data:application/json;base64,#{btoa unescape encodeURIComponent v3SourceMap}\n//# sourceURL=#{convertFilePath(filePath)}" - try - fs.writeFileSync(cachePath, js) - js - -requireCoffeeScript = (module, filePath) -> - coffee = fs.readFileSync(filePath, 'utf8') - cachePath = getCachePath(coffee) - js = getCachedJavaScript(cachePath) ? compileCoffeeScript(coffee, filePath, cachePath) - module._compile(js, filePath) - -module.exports = - cacheDir: cacheDir - - register: -> - Object.defineProperty(require.extensions, '.coffee', { - writable: false - value: requireCoffeeScript - }) - - addPathToCache: (filePath) -> - switch path.extname(filePath) - when '.coffee' - content = fs.readFileSync(filePath, 'utf8') - cachePath = getCachePath(coffee) - compileCoffeeScript(coffee, filePath, cachePath) - when '.cson' - CSON.readFileSync(filePath) - when '.js' - require('./6to5').addPathToCache(filePath) - - getCacheMisses: -> stats.misses - - getCacheHits: -> stats.hits diff --git a/src/compile-cache.coffee b/src/compile-cache.coffee new file mode 100644 index 000000000..ec85ae674 --- /dev/null +++ b/src/compile-cache.coffee @@ -0,0 +1,26 @@ +path = require 'path' +CSON = require 'season' +CoffeeCache = require 'coffee-cash' +to5 = require './6to5' + +# This file is required directly by apm so that files can be cached during +# package install so that the first package load in Atom doesn't have to +# compile anything. +exports.addPathToCache = (filePath, atomHome) -> + atomHome ?= process.env.ATOM_HOME + cacheDir = path.join(atomHome, 'compile-cache') + # Use separate compile cache when sudo'ing as root to avoid permission issues + if process.env.USER is 'root' and process.env.SUDO_USER and process.env.SUDO_USER isnt process.env.USER + cacheDir = path.join(cacheDir, 'root') + + CoffeeCache.setCacheDirectory(path.join(cacheDir, 'coffee')) + CSON.setCacheDir(path.join(cacheDir, 'cson')) + to5.setCacheDirectory(path.join(cacheDir, 'js')) + + switch path.extname(filePath) + when '.coffee' + CoffeeCache.addPathToCache(filePath) + when '.cson' + CSON.readFileSync(filePath) + when '.js' + to5.addPathToCache(filePath) diff --git a/src/task.coffee b/src/task.coffee index 42b744b6d..6fe055ca6 100644 --- a/src/task.coffee +++ b/src/task.coffee @@ -65,12 +65,15 @@ class Task # * `taskPath` The {String} path to the CoffeeScript/JavaScript file that # exports a single {Function} to execute. constructor: (taskPath) -> - coffeeCacheRequire = "require('#{require.resolve('./coffee-cache')}').register();" - coffeeScriptRequire = "require('#{require.resolve('coffee-script')}').register();" + coffeeCacheRequire = "require('#{require.resolve('coffee-cash')}')" + coffeeCachePath = require('coffee-cash').getCacheDirectory() + coffeeStackRequire = "require('#{require.resolve('coffeestack')}')" + stackCachePath = require('coffeestack').getCacheDirectory() taskBootstrapRequire = "require('#{require.resolve('./task-bootstrap')}');" bootstrap = """ - #{coffeeScriptRequire} - #{coffeeCacheRequire} + #{coffeeCacheRequire}.setCacheDirectory('#{coffeeCachePath}'); + #{coffeeCacheRequire}.register(); + #{coffeeStackRequire}.setCacheDirectory('#{stackCachePath}'); #{taskBootstrapRequire} """ bootstrap = bootstrap.replace(/\\/g, "\\\\") diff --git a/static/index.js b/static/index.js index e5f84f794..fb56a84bd 100644 --- a/static/index.js +++ b/static/index.js @@ -1,33 +1,17 @@ -function registerRuntimeTranspilers() { - // This sets require.extensions['.coffee']. - require('coffee-script').register(); - - // This redefines require.extensions['.js']. - require('../src/6to5').register(); -} +var fs = require('fs'); +var path = require('path'); window.onload = function() { try { var startTime = Date.now(); - var fs = require('fs'); - var path = require('path'); - // Ensure ATOM_HOME is always set before anything else is required - if (!process.env.ATOM_HOME) { - var home; - if (process.platform === 'win32') { - home = process.env.USERPROFILE; - } else { - home = process.env.HOME; - } - var atomHome = path.join(home, '.atom'); - try { - atomHome = fs.realpathSync(atomHome); - } catch (error) { - // Ignore since the path might just not exist yet. - } - process.env.ATOM_HOME = atomHome; + setupAtomHome(); + + var cacheDir = path.join(process.env.ATOM_HOME, 'compile-cache'); + // Use separate compile cache when sudo'ing as root to avoid permission issues + if (process.env.USER === 'root' && process.env.SUDO_USER && process.env.SUDO_USER !== process.env.USER) { + cacheDir = path.join(cacheDir, 'root'); } // Skip "?loadSettings=". @@ -45,10 +29,7 @@ window.onload = function() { var devMode = loadSettings.devMode || !loadSettings.resourcePath.startsWith(process.resourcesPath + path.sep); - // Require before the module cache in dev mode - if (devMode) { - registerRuntimeTranspilers(); - } + setupCoffeeCache(cacheDir); ModuleCache = require('../src/module-cache'); ModuleCache.register(loadSettings); @@ -65,11 +46,9 @@ window.onload = function() { require('vm-compatibility-layer'); - if (!devMode) { - registerRuntimeTranspilers(); - } - - require('../src/coffee-cache').register(); + setupCsonCache(cacheDir); + setupSourceMapCache(cacheDir); + setup6to5(cacheDir); require(loadSettings.bootstrapScript); require('ipc').sendChannel('window-command', 'window:loaded'); @@ -78,8 +57,7 @@ window.onload = function() { global.atom.loadTime = Date.now() - startTime; console.log('Window load time: ' + global.atom.getWindowLoadTime() + 'ms'); } - } - catch (error) { + } catch (error) { var currentWindow = require('remote').getCurrentWindow(); currentWindow.setSize(800, 600); currentWindow.center(); @@ -88,3 +66,41 @@ window.onload = function() { console.error(error.stack || error); } } + +var setupCoffeeCache = function(cacheDir) { + var CoffeeCache = require('coffee-cash'); + CoffeeCache.setCacheDirectory(path.join(cacheDir, 'coffee')); + CoffeeCache.register(); +} + +var setupAtomHome = function() { + if (!process.env.ATOM_HOME) { + var home; + if (process.platform === 'win32') { + home = process.env.USERPROFILE; + } else { + home = process.env.HOME; + } + var atomHome = path.join(home, '.atom'); + try { + atomHome = fs.realpathSync(atomHome); + } catch (error) { + // Ignore since the path might just not exist yet. + } + process.env.ATOM_HOME = atomHome; + } +} + +var setup6to5 = function(cacheDir) { + var to5 = require('../src/6to5'); + to5.setCacheDirectory(path.join(cacheDir, 'js', '6to5')); + to5.register(); +} + +var setupCsonCache = function(cacheDir) { + require('season').setCacheDir(path.join(cacheDir, 'cson')); +} + +var setupSourceMapCache = function(cacheDir) { + require('coffeestack').setCacheDirectory(path.join(cacheDir, 'coffee', 'source-maps')); +}