🍒(atom): Pull new, cleaner compile cache + index.js

This commit is contained in:
Ben Gotow 2015-11-17 19:43:00 -08:00
parent b814c54c88
commit f8539aa382
18 changed files with 684 additions and 393 deletions

1
.gitignore vendored
View file

@ -28,3 +28,4 @@ yoursway-create-dmg
*#
.idea/
spec-saved-state.json

View file

@ -27,7 +27,7 @@
"event-kit": "^1.0.2",
"fs-plus": "^2.3.2",
"fstream": "0.1.24",
"grim": "1.1.0",
"grim": "1.5.0",
"guid": "0.0.10",
"inflection": "^1.7",
"jasmine-json": "~0.0",
@ -64,6 +64,7 @@
"service-hub": "^0.2.0",
"space-pen": "3.8.2",
"spellchecker": "^3.1.2",
"source-map-support": "^0.3.2",
"temp": "^0.8",
"theorist": "^1.0",
"underscore": "^1.8",

View file

@ -13,7 +13,7 @@ try
require '../src/window'
NylasEnvConstructor = require '../src/nylas-env'
NylasEnvConstructor.configDirPath = fs.absolute('~/.nylas-spec')
window.NylasEnv = NylasEnvConstructor.loadOrCreate()
window.NylasEnv = window.atom = NylasEnvConstructor.loadOrCreate()
global.Promise.longStackTraces() if NylasEnv.inDevMode()
# Show window synchronously so a focusout doesn't fire on input elements

View file

@ -65,7 +65,7 @@ class Application
exit: (status) -> app.exit(status)
constructor: (options) ->
{@resourcePath, @version, @devMode, test, @safeMode} = options
{@resourcePath, @devResourcePath, @version, @devMode, test, @safeMode} = options
@specMode = test
# Normalize to make sure drive letter case is consistent on Windows
@ -479,7 +479,7 @@ class Application
resourcePath = @resourcePath
try
bootstrapScript = require.resolve(path.resolve(global.devResourcePath, 'spec', 'spec-bootstrap'))
bootstrapScript = require.resolve(path.resolve(@devResourcePath, 'spec', 'spec-bootstrap'))
catch error
bootstrapScript = require.resolve(path.resolve(__dirname, '..', '..', 'spec', 'spec-bootstrap'))

View file

@ -1,5 +1,9 @@
global.shellStartTime = Date.now()
process.on 'uncaughtException', (error={}) ->
console.log(error.message) if error.message?
console.log(error.stack) if error.stack?
app = require 'app'
fs = require 'fs-plus'
path = require 'path'
@ -7,15 +11,10 @@ optimist = require 'optimist'
start = ->
args = parseCommandLine()
global.errorLogger = setupErrorLogger(args)
setupCoffeeScript()
if process.platform is 'win32'
SquirrelUpdate = require './squirrel-update'
squirrelCommand = process.argv[1]
return if SquirrelUpdate.handleStartupEvent(app, squirrelCommand)
setupNylasHome(args)
setupCompileCache()
return if handleStartupEventWithSquirrel()
# This prevents Win10 from showing dupe items in the taskbar
app.setAppUserModelId('com.squirrel.nylas.nylas')
@ -24,7 +23,6 @@ start = ->
event.preventDefault()
args.pathsToOpen.push(pathToOpen)
args.urlsToOpen = []
addUrlToOpen = (event, urlToOpen) ->
event.preventDefault()
args.urlsToOpen.push(urlToOpen)
@ -39,25 +37,31 @@ start = ->
app.removeListener 'open-file', addPathToOpen
app.removeListener 'open-url', addUrlToOpen
cwd = args.executedFrom?.toString() or process.cwd()
args.pathsToOpen = args.pathsToOpen.map (pathToOpen) ->
if cwd
path.resolve(cwd, pathToOpen.toString())
else
path.resolve(pathToOpen.toString())
if args.devMode
require(path.join(args.resourcePath, 'src', 'coffee-cache')).register()
Application = require path.join(args.resourcePath, 'src', 'browser', 'application')
else
Application = require './application'
Application = require path.join(args.resourcePath, 'src', 'browser', 'application')
Application.open(args)
console.log("App load time: #{Date.now() - global.shellStartTime}ms") unless args.test
global.devResourcePath = process.env.N1_PATH ? process.cwd()
# Normalize to make sure drive letter case is consistent on Windows
global.devResourcePath = path.normalize(global.devResourcePath) if global.devResourcePath
setupNylasHome = ->
return if process.env.NYLAS_HOME
atomHome = path.join(app.getHomeDir(), '.nylas')
process.env.NYLAS_HOME = atomHome
normalizeDriveLetterName = (filePath) ->
if process.platform is 'win32'
filePath.replace /^([a-z]):/, ([driveLetter]) -> driveLetter.toUpperCase() + ":"
else
filePath
handleStartupEventWithSquirrel = ->
return false unless process.platform is 'win32'
SquirrelUpdate = require './squirrel-update'
squirrelCommand = process.argv[1]
SquirrelUpdate.handleStartupEvent(app, squirrelCommand)
setupCompileCache = ->
compileCache = require('../compile-cache')
compileCache.setHomeDirectory(process.env.NYLAS_HOME)
setupErrorLogger = (args={}) ->
ErrorLogger = require '../error-logger'
@ -71,15 +75,6 @@ setupCrashReporter = ->
# but for now let's not send them to GitHub
# crashReporter.start(productName: "N1", companyName: "Nylas")
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)
parseCommandLine = ->
version = app.getVersion()
options = optimist(process.argv[1..])
@ -120,17 +115,19 @@ parseCommandLine = ->
process.stdout.write("#{version}\n")
process.exit(0)
executedFrom = args['executed-from']
executedFrom = args['executed-from']?.toString() ? process.cwd()
devMode = args['dev']
safeMode = args['safe']
pathsToOpen = args._
pathsToOpen = [executedFrom] if executedFrom and pathsToOpen.length is 0
urlsToOpen = []
test = args['test']
specDirectory = args['spec-directory']
newWindow = args['new-window']
pidToKillWhenClosed = args['pid'] if args['wait']
logFile = args['log-file']
specFilePattern = args['file-pattern']
devResourcePath = process.env.N1_PATH ? process.cwd()
if args['resource-path']
devMode = true
@ -157,8 +154,8 @@ parseCommandLine = ->
else
specDirectory = path.resolve(path.join(global.devResourcePath, "internal_packages", test))
if devMode
resourcePath ?= global.devResourcePath
devMode = true if test
resourcePath ?= devResourcePath if devMode
unless fs.statSyncNoException(resourcePath)
resourcePath = path.dirname(path.dirname(__dirname))
@ -167,6 +164,9 @@ parseCommandLine = ->
# explicitly pass it by command line, see http://git.io/YC8_Ew.
process.env.PATH = args['path-environment'] if args['path-environment']
{resourcePath, pathsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, safeMode, newWindow, specDirectory, specsOnCommandLine, logFile, specFilePattern}
resourcePath = normalizeDriveLetterName(resourcePath)
devResourcePath = normalizeDriveLetterName(devResourcePath)
{resourcePath, pathsToOpen, urlsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, safeMode, newWindow, specDirectory, specsOnCommandLine, logFile, specFilePattern}
start()

View file

@ -1,203 +1,220 @@
// Generated by CoffeeScript 1.9.2
(function() {
var app, fs, optimist, parseCommandLine, path, ref, setupCoffeeScript, setupCrashReporter, setupErrorLogger, start;
var app, fs, handleStartupEventWithSquirrel, normalizeDriveLetterName, optimist, parseCommandLine, path, setupCompileCache, setupCrashReporter, setupErrorLogger, setupNylasHome, start;
global.shellStartTime = Date.now();
global.shellStartTime = Date.now();
app = require('app');
fs = require('fs-plus');
path = require('path');
optimist = require('optimist');
start = function() {
var SquirrelUpdate, addPathToOpen, addUrlToOpen, args, squirrelCommand;
args = parseCommandLine();
global.errorLogger = setupErrorLogger(args);
setupCoffeeScript();
if (process.platform === 'win32') {
SquirrelUpdate = require('./squirrel-update');
squirrelCommand = process.argv[1];
if (SquirrelUpdate.handleStartupEvent(app, squirrelCommand)) {
return;
}
}
app.setAppUserModelId('com.squirrel.nylas.nylas');
addPathToOpen = function(event, pathToOpen) {
event.preventDefault();
return args.pathsToOpen.push(pathToOpen);
};
args.urlsToOpen = [];
addUrlToOpen = function(event, urlToOpen) {
event.preventDefault();
return args.urlsToOpen.push(urlToOpen);
};
app.on('open-file', addPathToOpen);
app.on('open-url', addUrlToOpen);
app.on('will-finish-launching', function() {
return setupCrashReporter();
});
return app.on('ready', function() {
var Application, cwd, ref;
app.removeListener('open-file', addPathToOpen);
app.removeListener('open-url', addUrlToOpen);
cwd = ((ref = args.executedFrom) != null ? ref.toString() : void 0) || process.cwd();
args.pathsToOpen = args.pathsToOpen.map(function(pathToOpen) {
if (cwd) {
return path.resolve(cwd, pathToOpen.toString());
} else {
return path.resolve(pathToOpen.toString());
}
});
if (args.devMode) {
require(path.join(args.resourcePath, 'src', 'coffee-cache')).register();
Application = require(path.join(args.resourcePath, 'src', 'browser', 'application'));
} else {
Application = require('./application');
}
Application.open(args);
if (!args.test) {
return console.log("App load time: " + (Date.now() - global.shellStartTime) + "ms");
}
});
};
global.devResourcePath = (ref = process.env.N1_PATH) != null ? ref : process.cwd();
if (global.devResourcePath) {
global.devResourcePath = path.normalize(global.devResourcePath);
process.on('uncaughtException', function(error) {
if (error == null) {
error = {};
}
if (error.message != null) {
console.log(error.message);
}
if (error.stack != null) {
return console.log(error.stack);
}
});
setupErrorLogger = function(args) {
var ErrorLogger;
if (args == null) {
args = {};
app = require('app');
fs = require('fs-plus');
path = require('path');
optimist = require('optimist');
start = function() {
var addPathToOpen, addUrlToOpen, args;
args = parseCommandLine();
global.errorLogger = setupErrorLogger(args);
setupNylasHome(args);
setupCompileCache();
if (handleStartupEventWithSquirrel()) {
return;
}
app.setAppUserModelId('com.squirrel.nylas.nylas');
addPathToOpen = function(event, pathToOpen) {
event.preventDefault();
return args.pathsToOpen.push(pathToOpen);
};
addUrlToOpen = function(event, urlToOpen) {
event.preventDefault();
return args.urlsToOpen.push(urlToOpen);
};
app.on('open-file', addPathToOpen);
app.on('open-url', addUrlToOpen);
app.on('will-finish-launching', function() {
return setupCrashReporter();
});
return app.on('ready', function() {
var Application;
app.removeListener('open-file', addPathToOpen);
app.removeListener('open-url', addUrlToOpen);
Application = require(path.join(args.resourcePath, 'src', 'browser', 'application'));
Application.open(args);
if (!args.test) {
return console.log("App load time: " + (Date.now() - global.shellStartTime) + "ms");
}
ErrorLogger = require('../error-logger');
return new ErrorLogger({
inSpecMode: args.test,
inDevMode: args.devMode,
resourcePath: args.resourcePath
});
};
setupNylasHome = function() {
var atomHome;
if (process.env.NYLAS_HOME) {
return;
}
atomHome = path.join(app.getHomeDir(), '.nylas');
return process.env.NYLAS_HOME = atomHome;
};
normalizeDriveLetterName = function(filePath) {
if (process.platform === 'win32') {
return filePath.replace(/^([a-z]):/, function(arg) {
var driveLetter;
driveLetter = arg[0];
return driveLetter.toUpperCase() + ":";
});
};
} else {
return filePath;
}
};
setupCrashReporter = function() {};
handleStartupEventWithSquirrel = function() {
var SquirrelUpdate, squirrelCommand;
if (process.platform !== 'win32') {
return false;
}
SquirrelUpdate = require('./squirrel-update');
squirrelCommand = process.argv[1];
return SquirrelUpdate.handleStartupEvent(app, squirrelCommand);
};
setupCoffeeScript = function() {
var CoffeeScript;
CoffeeScript = null;
return require.extensions['.coffee'] = function(module, filePath) {
var coffee, js;
if (CoffeeScript == null) {
CoffeeScript = require('coffee-script');
}
coffee = fs.readFileSync(filePath, 'utf8');
js = CoffeeScript.compile(coffee, {
filename: filePath
});
return module._compile(js, filePath);
};
};
setupCompileCache = function() {
var compileCache;
compileCache = require('../compile-cache');
return compileCache.setHomeDirectory(process.env.NYLAS_HOME);
};
parseCommandLine = function() {
var args, devMode, executedFrom, logFile, newWindow, options, packageDirectoryPath, packageManifest, packageManifestPath, pathsToOpen, pidToKillWhenClosed, resourcePath, safeMode, specDirectory, specFilePattern, specsOnCommandLine, test, version;
version = app.getVersion();
options = optimist(process.argv.slice(1));
options.usage("N1 v" + version + "\n\nUsage: n1 [options] [path ...]\n\nOne or more paths to files or folders to open may be specified.\n\nFile paths will open in the current window.\n\nFolder paths will open in an existing window if that folder has already been\nopened or a new window if it hasn't.\n\nEnvironment Variables:\nN1_PATH The path from which N1 loads source code in dev mode.\n Defaults to `cwd`.");
options.alias('d', 'dev').boolean('d').describe('d', 'Run in development mode.');
options.alias('f', 'foreground').boolean('f').describe('f', 'Keep the browser process in the foreground.');
options.alias('h', 'help').boolean('h').describe('h', 'Print this usage message.');
options.alias('l', 'log-file').string('l').describe('l', 'Log all output to file.');
options.alias('n', 'new-window').boolean('n').describe('n', 'Open a new window.');
options.alias('r', 'resource-path').string('r').describe('r', 'Set the path to the N1 source directory and enable dev-mode.');
options.alias('s', 'spec-directory').string('s').describe('s', 'Set the directory from which to run package specs (default: N1\'s spec directory).');
options.boolean('safe').describe('safe', 'Do not load packages from ~/.nylas/packages or ~/.nylas/dev/packages.');
options.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.');
options.alias('v', 'version').boolean('v').describe('v', 'Print the version.');
options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.');
args = options.argv;
if (args.help) {
process.stdout.write(options.help());
process.exit(0);
}
if (args.version) {
process.stdout.write(version + "\n");
process.exit(0);
}
executedFrom = args['executed-from'];
devMode = args['dev'];
safeMode = args['safe'];
pathsToOpen = args._;
if (executedFrom && pathsToOpen.length === 0) {
pathsToOpen = [executedFrom];
}
test = args['test'];
specDirectory = args['spec-directory'];
newWindow = args['new-window'];
if (args['wait']) {
pidToKillWhenClosed = args['pid'];
}
logFile = args['log-file'];
specFilePattern = args['file-pattern'];
if (args['resource-path']) {
devMode = true;
resourcePath = args['resource-path'];
} else {
specsOnCommandLine = true;
if (specDirectory != null) {
packageDirectoryPath = path.resolve(specDirectory, '..');
packageManifestPath = path.join(packageDirectoryPath, 'package.json');
if (fs.statSyncNoException(packageManifestPath)) {
try {
packageManifest = JSON.parse(fs.readFileSync(packageManifestPath));
if (packageManifest.name === 'edgehill') {
resourcePath = packageDirectoryPath;
}
} catch (_error) {}
}
} else {
if (test && toString.call(test) === "[object String]") {
if (test === "core") {
specDirectory = path.join(global.devResourcePath, "spec");
} else if (test === "window") {
specDirectory = path.join(global.devResourcePath, "spec");
specsOnCommandLine = false;
} else {
specDirectory = path.resolve(path.join(global.devResourcePath, "internal_packages", test));
setupErrorLogger = function(args) {
var ErrorLogger;
if (args == null) {
args = {};
}
ErrorLogger = require('../error-logger');
return new ErrorLogger({
inSpecMode: args.test,
inDevMode: args.devMode,
resourcePath: args.resourcePath
});
};
setupCrashReporter = function() {};
parseCommandLine = function() {
var args, devMode, devResourcePath, executedFrom, logFile, newWindow, options, packageDirectoryPath, packageManifest, packageManifestPath, pathsToOpen, pidToKillWhenClosed, ref, ref1, ref2, resourcePath, safeMode, specDirectory, specFilePattern, specsOnCommandLine, test, urlsToOpen, version;
version = app.getVersion();
options = optimist(process.argv.slice(1));
options.usage("N1 v" + version + "\n\nUsage: n1 [options] [path ...]\n\nOne or more paths to files or folders to open may be specified.\n\nFile paths will open in the current window.\n\nFolder paths will open in an existing window if that folder has already been\nopened or a new window if it hasn't.\n\nEnvironment Variables:\nN1_PATH The path from which N1 loads source code in dev mode.\n Defaults to `cwd`.");
options.alias('d', 'dev').boolean('d').describe('d', 'Run in development mode.');
options.alias('f', 'foreground').boolean('f').describe('f', 'Keep the browser process in the foreground.');
options.alias('h', 'help').boolean('h').describe('h', 'Print this usage message.');
options.alias('l', 'log-file').string('l').describe('l', 'Log all output to file.');
options.alias('n', 'new-window').boolean('n').describe('n', 'Open a new window.');
options.alias('r', 'resource-path').string('r').describe('r', 'Set the path to the N1 source directory and enable dev-mode.');
options.alias('s', 'spec-directory').string('s').describe('s', 'Set the directory from which to run package specs (default: N1\'s spec directory).');
options.boolean('safe').describe('safe', 'Do not load packages from ~/.nylas/packages or ~/.nylas/dev/packages.');
options.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.');
options.alias('v', 'version').boolean('v').describe('v', 'Print the version.');
options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.');
args = options.argv;
if (args.help) {
process.stdout.write(options.help());
process.exit(0);
}
if (args.version) {
process.stdout.write(version + "\n");
process.exit(0);
}
executedFrom = (ref = (ref1 = args['executed-from']) != null ? ref1.toString() : void 0) != null ? ref : process.cwd();
devMode = args['dev'];
safeMode = args['safe'];
pathsToOpen = args._;
if (executedFrom && pathsToOpen.length === 0) {
pathsToOpen = [executedFrom];
}
urlsToOpen = [];
test = args['test'];
specDirectory = args['spec-directory'];
newWindow = args['new-window'];
if (args['wait']) {
pidToKillWhenClosed = args['pid'];
}
logFile = args['log-file'];
specFilePattern = args['file-pattern'];
devResourcePath = (ref2 = process.env.N1_PATH) != null ? ref2 : process.cwd();
if (args['resource-path']) {
devMode = true;
resourcePath = args['resource-path'];
} else {
specsOnCommandLine = true;
if (specDirectory != null) {
packageDirectoryPath = path.resolve(specDirectory, '..');
packageManifestPath = path.join(packageDirectoryPath, 'package.json');
if (fs.statSyncNoException(packageManifestPath)) {
try {
packageManifest = JSON.parse(fs.readFileSync(packageManifestPath));
if (packageManifest.name === 'edgehill') {
resourcePath = packageDirectoryPath;
}
}
} catch (_error) {}
}
if (devMode) {
if (resourcePath == null) {
resourcePath = global.devResourcePath;
} else {
if (test && toString.call(test) === "[object String]") {
if (test === "core") {
specDirectory = path.join(global.devResourcePath, "spec");
} else if (test === "window") {
specDirectory = path.join(global.devResourcePath, "spec");
specsOnCommandLine = false;
} else {
specDirectory = path.resolve(path.join(global.devResourcePath, "internal_packages", test));
}
}
}
if (!fs.statSyncNoException(resourcePath)) {
resourcePath = path.dirname(path.dirname(__dirname));
}
if (test) {
devMode = true;
}
if (devMode) {
if (resourcePath == null) {
resourcePath = devResourcePath;
}
if (args['path-environment']) {
process.env.PATH = args['path-environment'];
}
return {
resourcePath: resourcePath,
pathsToOpen: pathsToOpen,
executedFrom: executedFrom,
test: test,
version: version,
pidToKillWhenClosed: pidToKillWhenClosed,
devMode: devMode,
safeMode: safeMode,
newWindow: newWindow,
specDirectory: specDirectory,
specsOnCommandLine: specsOnCommandLine,
logFile: logFile,
specFilePattern: specFilePattern
};
}
if (!fs.statSyncNoException(resourcePath)) {
resourcePath = path.dirname(path.dirname(__dirname));
}
if (args['path-environment']) {
process.env.PATH = args['path-environment'];
}
resourcePath = normalizeDriveLetterName(resourcePath);
devResourcePath = normalizeDriveLetterName(devResourcePath);
return {
resourcePath: resourcePath,
pathsToOpen: pathsToOpen,
urlsToOpen: urlsToOpen,
executedFrom: executedFrom,
test: test,
version: version,
pidToKillWhenClosed: pidToKillWhenClosed,
devMode: devMode,
safeMode: safeMode,
newWindow: newWindow,
specDirectory: specDirectory,
specsOnCommandLine: specsOnCommandLine,
logFile: logFile,
specFilePattern: specFilePattern
};
};
start();
start();
}).call(this);
// ---
// generated by coffee-script 1.9.2

View file

@ -1,73 +0,0 @@
crypto = require 'crypto'
path = require 'path'
CoffeeScript = require 'coffee-script'
CSON = require 'season'
fs = require 'fs-plus'
cacheDir = path.join(fs.absolute('~/.nylas'), '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', {
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)
getCacheMisses: -> stats.misses
getCacheHits: -> stats.hits

208
src/compile-cache.js Normal file
View file

@ -0,0 +1,208 @@
'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,
'.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 cachePath = path.join(cacheDirectory, relativeCachePath)
fs.writeFileSync(cachePath, code, 'utf8')
}
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
}
}
})
var sourceMapPrepareStackTrace = Error.prepareStackTrace
var prepareStackTrace = sourceMapPrepareStackTrace
// Prevent coffee-script from reassigning Error.prepareStackTrace
Object.defineProperty(Error, 'prepareStackTrace', {
get: function () { return prepareStackTrace },
set: function (newValue) {}
})
// Enable Grim to access the raw stack without reassigning Error.prepareStackTrace
Error.prototype.getRawStack = function () { // eslint-disable-line no-extend-native
prepareStackTrace = getRawStack
var result = this.stack
prepareStackTrace = sourceMapPrepareStackTrace
return result
}
function getRawStack (_, stack) {
return stack
}
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()

View file

@ -0,0 +1,66 @@
'use strict'
var crypto = require('crypto')
var path = require('path')
var defaultOptions = require('../../static/babelrc.json')
var babel = null
var babelVersionDirectory = null
var PREFIXES = [
'/** @babel */',
'"use babel"',
'\'use babel\''
]
var PREFIX_LENGTH = Math.max.apply(Math, PREFIXES.map(function (prefix) {
return prefix.length
}))
exports.shouldCompile = function (sourceCode, filePath) {
if (filePath.endsWith('.es6')) {
return true;
}
var start = sourceCode.substr(0, PREFIX_LENGTH)
return PREFIXES.some(function (prefix) {
return start.indexOf(prefix) === 0
});
}
exports.getCachePath = function (sourceCode) {
if (babelVersionDirectory == null) {
var babelVersion = require('babel-core/package.json').version
babelVersionDirectory = path.join('js', 'babel', createVersionAndOptionsDigest(babelVersion, defaultOptions))
}
return path.join(
babelVersionDirectory,
crypto
.createHash('sha1')
.update(sourceCode, 'utf8')
.digest('hex') + '.js'
)
}
exports.compile = function (sourceCode, filePath) {
if (!babel) {
babel = require('babel-core')
}
var options = {filename: filePath}
for (var key in defaultOptions) {
options[key] = defaultOptions[key]
}
return babel.transform(sourceCode, options).code
}
function createVersionAndOptionsDigest (version, options) {
return crypto
.createHash('sha1')
.update('babel-core', 'utf8')
.update('\0', 'utf8')
.update(version, 'utf8')
.update('\0', 'utf8')
.update(JSON.stringify(options), 'utf8')
.digest('hex')
}

View file

@ -0,0 +1,48 @@
'use strict'
var crypto = require('crypto')
var path = require('path')
var CoffeeScript = null
exports.shouldCompile = function () {
return true
}
exports.getCachePath = function (sourceCode) {
return path.join(
'coffee',
crypto
.createHash('sha1')
.update(sourceCode, 'utf8')
.digest('hex') + '.js'
)
}
exports.compile = function (sourceCode, filePath) {
if (!CoffeeScript) {
var previousPrepareStackTrace = Error.prepareStackTrace
CoffeeScript = require('coffee-react')
// When it loads, coffee-script reassigns Error.prepareStackTrace. We have
// already reassigned it via the 'source-map-support' module, so we need
// to set it back.
Error.prepareStackTrace = previousPrepareStackTrace
}
if (process.platform === 'win32') {
filePath = 'file:///' + path.resolve(filePath).replace(/\\/g, '/')
}
var output = CoffeeScript.compile(sourceCode, {
filename: filePath,
sourceFiles: [filePath],
sourceMap: true
})
var js = output.js
js += '\n'
js += '//# sourceMappingURL=data:application/json;base64,'
js += new Buffer(output.v3SourceMap).toString('base64')
js += '\n'
return js
}

View file

@ -0,0 +1,53 @@
'use strict'
var _ = require('underscore')
var crypto = require('crypto')
var path = require('path')
var defaultOptions = {
target: 1,
module: 'commonjs',
sourceMap: true
}
var TypeScriptSimple = null
var typescriptVersionDir = null
exports.shouldCompile = function () {
return true
}
exports.getCachePath = function (sourceCode) {
if (typescriptVersionDir == null) {
var version = require('typescript-simple/package.json').version
typescriptVersionDir = path.join('ts', createVersionAndOptionsDigest(version, defaultOptions))
}
return path.join(
typescriptVersionDir,
crypto
.createHash('sha1')
.update(sourceCode, 'utf8')
.digest('hex') + '.js'
)
}
exports.compile = function (sourceCode, filePath) {
if (!TypeScriptSimple) {
TypeScriptSimple = require('typescript-simple').TypeScriptSimple
}
var options = _.defaults({filename: filePath}, defaultOptions)
return new TypeScriptSimple(options, false).compile(sourceCode, filePath)
}
function createVersionAndOptionsDigest (version, options) {
return crypto
.createHash('sha1')
.update('typescript', 'utf8')
.update('\0', 'utf8')
.update(version, 'utf8')
.update('\0', 'utf8')
.update(JSON.stringify(options), 'utf8')
.digest('hex')
}

View file

@ -1,15 +0,0 @@
module.exports =
class ItemRegistry
constructor: ->
@items = new WeakSet
addItem: (item) ->
if @hasItem(item)
throw new Error("The workspace can only contain one instance of item #{item}")
@items.add(item)
removeItem: (item) ->
@items.delete(item)
hasItem: (item) ->
@items.has(item)

View file

@ -1,14 +1,10 @@
path = require 'path'
fs = require 'fs-plus'
LessCache = require 'less-cache'
{Subscriber} = require 'emissary'
# {LessCache} wrapper used by {ThemeManager} to read stylesheets.
module.exports =
class LessCompileCache
Subscriber.includeInto(this)
@cacheDir: path.join(require('./coffee-cache').cacheDir, 'less')
@cacheDir: path.join(process.env.NYLAS_HOME, 'compile-cache', 'less')
constructor: ({resourcePath, importPaths}) ->
@lessSearchPaths = [
@ -35,5 +31,3 @@ class LessCompileCache
cssForFile: (stylesheetPath, lessContent) ->
@cache.cssForFile(stylesheetPath, lessContent)
destroy: -> @unsubscribe()

View file

@ -177,7 +177,7 @@ class NylasEnvConstructor extends Model
process.env.NODE_ENV ?= 'production' unless devMode
# Set NylasEnv's home so packages don't have to guess it
process.env.ATOM_HOME = configDirPath
process.env.NYLAS_HOME = configDirPath
# Setup config and load it immediately so it's available to our singletons
@config = new Config({configDirPath, resourcePath})
@ -234,27 +234,20 @@ class NylasEnvConstructor extends Model
@lastUncaughtError = Array::slice.call(arguments)
[message, url, line, column, originalError] = @lastUncaughtError
# Convert the javascript error back into a Coffeescript error
convertedLine = convertLine(url, line, column, sourceMapCache)
{line, column} = convertedLine if convertedLine?
originalError.stack = convertStackTrace(originalError.stack, sourceMapCache) if originalError
{line, column} = mapSourcePosition({source: url, line, column})
eventObject = {message, url, line, column, originalError}
openDevTools = true
eventObject.preventDefault = -> openDevTools = false
# Announce that we will display the error. Recipients can call preventDefault
# to prevent the developer tools from being shown
@emitter.emit('will-throw-error', eventObject)
@emitter.emit 'will-throw-error', eventObject
if openDevTools and @inDevMode()
@openDevTools()
@executeJavaScriptInDevTools('InspectorFrontendAPI.showConsole()')
@executeJavaScriptInDevTools('DevToolsAPI.showConsole()')
# Announce that the error was uncaught
@emit('uncaught-error', arguments...)
@emitter.emit('did-throw-error', eventObject)
@emitter.emit 'did-throw-error', {message, url, line, column, originalError}
# Since Bluebird is the promise library, we can properly report
# unhandled errors from business logic inside promises.

View file

@ -6,7 +6,7 @@ global.Promise = require 'bluebird'
require './window'
NylasEnvConstructor = require './nylas-env'
window.NylasEnv = NylasEnvConstructor.loadOrCreate()
window.NylasEnv = window.atom = NylasEnvConstructor.loadOrCreate()
global.Promise.longStackTraces() if NylasEnv.inDevMode()
NylasEnv.initialize()
NylasEnv.startRootWindow()

View file

@ -10,7 +10,7 @@ require './window'
# {windowType} = loadSettings
NylasEnvConstructor = require './nylas-env'
window.NylasEnv = NylasEnvConstructor.loadOrCreate()
window.NylasEnv = window.atom = NylasEnvConstructor.loadOrCreate()
global.Promise.longStackTraces() if NylasEnv.inDevMode()
NylasEnv.initialize()
NylasEnv.startSecondaryWindow()

View file

@ -3,23 +3,62 @@
// global scope. We need to do it here before React loads.
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {}
var path = require('path');
function registerRuntimeTranspilers(hotreload) {
// This sets require.extensions['.coffee'].
require('coffee-script').register();
// This sets require.extensions['.cjsx']
if (hotreload) {
// This is custom built to look at window.devMode and enable hot-reloading
require('../src/cjsx').register();
} else {
require('coffee-react/register');
function setLoadTime (loadTime) {
if (global.NylasEnv) {
global.NylasEnv.loadTime = loadTime
console.log('Window load time: ' + global.NylasEnv.getWindowLoadTime() + 'ms')
}
// This redefines require.extensions
babelOptions = require('./babelrc.json');
require('babel-core/register')(babelOptions);
}
function handleSetupError (error) {
var currentWindow = require('remote').getCurrentWindow()
currentWindow.setSize(800, 600)
currentWindow.center()
currentWindow.show()
currentWindow.openDevTools()
console.error(error.stack || error)
}
function setupWindow (loadSettings) {
var hotreload = loadSettings.devMode && !loadSettings.isSpec;
var CompileCache = require('../src/compile-cache')
CompileCache.setHotReload(hotreload)
CompileCache.setHomeDirectory(process.env.NYLAS_HOME)
var ModuleCache = require('../src/module-cache')
ModuleCache.register(loadSettings)
ModuleCache.add(loadSettings.resourcePath)
// Start the crash reporter before anything else.
require('crash-reporter').start({
productName: 'N1',
companyName: 'Nylas',
// By explicitly passing the app version here, we could save the call
// of "require('remote').require('app').getVersion()".
extra: {_version: loadSettings.appVersion}
})
setupVmCompatibility()
setupCsonCache(CompileCache.getCacheDirectory())
require(loadSettings.bootstrapScript)
require('ipc').sendChannel('window-command', 'window:loaded')
}
function setupCsonCache (cacheDir) {
require('season').setCacheDir(path.join(cacheDir, 'cson'))
}
function setupVmCompatibility () {
var vm = require('vm')
if (!vm.Script.createContext) {
vm.Script.createContext = vm.createContext
}
}
window.onload = function() {
try {
var startTime = Date.now();
@ -44,51 +83,10 @@ window.onload = function() {
// Normalize to make sure drive letter case is consistent on Windows
process.resourcesPath = path.normalize(process.resourcesPath);
var registerBeforeModuleCache = loadSettings.devMode || !loadSettings.resourcePath.startsWith(process.resourcesPath + path.sep);
var hotreload = loadSettings.devMode && !loadSettings.isSpec;
// Require before the module cache in dev mode
if (registerBeforeModuleCache) {
registerRuntimeTranspilers(hotreload);
}
ModuleCache = require('../src/module-cache');
ModuleCache.register(loadSettings);
ModuleCache.add(loadSettings.resourcePath);
// Start the crash reporter before anything else.
require('crash-reporter').start({
productName: 'Nylas N1',
companyName: 'Nylas',
// By explicitly passing the app version here, we could save the call
// of "require('remote').require('app').getVersion()".
extra: {_version: loadSettings.appVersion}
});
require('vm-compatibility-layer');
if (!registerBeforeModuleCache) {
registerRuntimeTranspilers(hotreload);
}
require('../src/coffee-cache').register();
require(loadSettings.bootstrapScript);
if (global.NylasEnv) {
global.NylasEnv.loadTime = Date.now() - startTime;
console.log('Window load time: ' + global.NylasEnv.getWindowLoadTime() + 'ms');
} else {
require('ipc').sendChannel('window-command', 'window:loaded');
}
setupWindow(loadSettings)
setLoadTime(Date.now() - startTime)
}
catch (error) {
var currentWindow = require('remote').getCurrentWindow();
currentWindow.setSize(800, 600);
currentWindow.center();
currentWindow.show();
currentWindow.openDevTools();
console.error(error.stack || error);
console.error(error.message, error);
handleSetupError(error)
}
}