diff --git a/packages/client-app/spec/buffered-process-spec.coffee b/packages/client-app/spec/buffered-process-spec.coffee deleted file mode 100644 index 8794fc22b..000000000 --- a/packages/client-app/spec/buffered-process-spec.coffee +++ /dev/null @@ -1,75 +0,0 @@ -ChildProcess = require 'child_process' -path = require 'path' -BufferedProcess = require '../src/buffered-process' - -describe "BufferedProcess", -> - describe "when a bad command is specified", -> - [oldOnError] = [] - beforeEach -> - oldOnError = window.onerror - window.onerror = jasmine.createSpy() - - afterEach -> - window.onerror = oldOnError - - describe "when there is an error handler specified", -> - it "calls the error handler and does not throw an exception", -> - p = new BufferedProcess - command: 'bad-command-nope' - args: ['nothing'] - options: {} - - errorSpy = jasmine.createSpy().andCallFake (error) -> error.handle() - p.onWillThrowError(errorSpy) - - waitsFor -> errorSpy.callCount > 0 - - runs -> - expect(window.onerror).not.toHaveBeenCalled() - expect(errorSpy).toHaveBeenCalled() - expect(errorSpy.mostRecentCall.args[0].error.message).toContain 'spawn bad-command-nope ENOENT' - - # describe "when there is not an error handler specified", -> - # it "calls the error handler and does not throw an exception", -> - # spyOn(process, "nextTick").andCallFake (fn) -> fn() - # - # try - # p = new BufferedProcess - # command: 'bad-command-nope' - # args: ['nothing'] - # options: {stdout: 'ignore'} - # - # catch error - # expect(error.message).toContain 'Failed to spawn command `bad-command-nope`' - # expect(error.name).toBe 'BufferedProcessError' - - describe "on Windows", -> - originalPlatform = null - - beforeEach -> - # Prevent any commands from actually running and affecting the host - originalSpawn = ChildProcess.spawn - spyOn(ChildProcess, 'spawn').andCallFake -> - # Just spawn something that won't actually modify the host - if originalPlatform is 'win32' - originalSpawn('dir') - else - originalSpawn('ls') - - originalPlatform = process.platform - Object.defineProperty process, 'platform', value: 'win32' - - afterEach -> - Object.defineProperty process, 'platform', value: originalPlatform - - describe "when the explorer command is spawned on Windows", -> - it "doesn't quote arguments of the form /root,C...", -> - new BufferedProcess({command: 'explorer.exe', args: ['/root,C:\\foo']}) - expect(ChildProcess.spawn.argsForCall[0][1][2]).toBe '"explorer.exe /root,C:\\foo"' - - it "spawns the command using a cmd.exe wrapper", -> - new BufferedProcess({command: 'dir'}) - expect(path.basename(ChildProcess.spawn.argsForCall[0][0])).toBe 'cmd.exe' - expect(ChildProcess.spawn.argsForCall[0][1][0]).toBe '/s' - expect(ChildProcess.spawn.argsForCall[0][1][1]).toBe '/c' - expect(ChildProcess.spawn.argsForCall[0][1][2]).toBe '"dir"' diff --git a/packages/client-app/spec/n1-spec-runner/n1-spec-runner.es6 b/packages/client-app/spec/n1-spec-runner/n1-spec-runner.es6 index 984bf2fc2..3a69803ae 100644 --- a/packages/client-app/spec/n1-spec-runner/n1-spec-runner.es6 +++ b/packages/client-app/spec/n1-spec-runner/n1-spec-runner.es6 @@ -120,9 +120,6 @@ class N1SpecRunner { NylasEnv.storeWindowDimensions(); return NylasEnv.saveSync(); }); - - // On load this will extend the window object - require('../../src/window'); } _addReporters() { diff --git a/packages/client-app/src/buffered-process.coffee b/packages/client-app/src/buffered-process.coffee deleted file mode 100644 index ea815de4e..000000000 --- a/packages/client-app/src/buffered-process.coffee +++ /dev/null @@ -1,244 +0,0 @@ -_ = require 'underscore' -ChildProcess = require 'child_process' -{Emitter} = require 'event-kit' -path = require 'path' - -# Extended: A wrapper which provides standard error/output line buffering for -# Node's ChildProcess. -# -# ## Examples -# -# ```coffee -# {BufferedProcess} = require 'nylas-exports' -# -# command = 'ps' -# args = ['-ef'] -# stdout = (output) -> console.log(output) -# exit = (code) -> console.log("ps -ef exited with #{code}") -# process = new BufferedProcess({command, args, stdout, exit}) -# ``` -module.exports = -class BufferedProcess - ### - Section: Construction - ### - - # Public: Runs the given command by spawning a new child process. - # - # * `options` An {Object} with the following keys: - # * `command` The {String} command to execute. - # * `args` The {Array} of arguments to pass to the command (optional). - # * `options` {Object} (optional) The options {Object} to pass to Node's - # `ChildProcess.spawn` method. - # * `stdout` {Function} (optional) The callback that receives a single - # argument which contains the standard output from the command. The - # callback is called as data is received but it's buffered to ensure only - # complete lines are passed until the source stream closes. After the - # source stream has closed all remaining data is sent in a final call. - # * `data` {String} - # * `stderr` {Function} (optional) The callback that receives a single - # argument which contains the standard error output from the command. The - # callback is called as data is received but it's buffered to ensure only - # complete lines are passed until the source stream closes. After the - # source stream has closed all remaining data is sent in a final call. - # * `data` {String} - # * `exit` {Function} (optional) The callback which receives a single - # argument containing the exit status. - # * `code` {Number} - constructor: ({command, args, options, stdout, stderr, exit}={}) -> - @emitter = new Emitter - options ?= {} - @command = command - # Related to joyent/node#2318 - if process.platform is 'win32' - # Quote all arguments and escapes inner quotes - if args? - cmdArgs = args.filter (arg) -> arg? - cmdArgs = cmdArgs.map (arg) => - if @isExplorerCommand(command) and /^\/[a-zA-Z]+,.*$/.test(arg) - # Don't wrap /root,C:\folder style arguments to explorer calls in - # quotes since they will not be interpreted correctly if they are - arg - else - "\"#{arg.toString().replace(/"/g, '\\"')}\"" - else - cmdArgs = [] - if /\s/.test(command) - cmdArgs.unshift("\"#{command}\"") - else - cmdArgs.unshift(command) - cmdArgs = ['/s', '/c', "\"#{cmdArgs.join(' ')}\""] - cmdOptions = _.clone(options) - cmdOptions.windowsVerbatimArguments = true - @spawn(@getCmdPath(), cmdArgs, cmdOptions) - else - @spawn(command, args, options) - - @killed = false - @handleEvents(stdout, stderr, exit) - - ### - Section: Event Subscription - ### - - # Public: Will call your callback when an error will be raised by the process. - # Usually this is due to the command not being available or not on the PATH. - # You can call `handle()` on the object passed to your callback to indicate - # that you have handled this error. - # - # * `callback` {Function} callback - # * `errorObject` {Object} - # * `error` {Object} the error object - # * `handle` {Function} call this to indicate you have handled the error. - # The error will not be thrown if this function is called. - # - # Returns a {Disposable} - onWillThrowError: (callback) -> - @emitter.on 'will-throw-error', callback - - ### - Section: Helper Methods - ### - - # Helper method to pass data line by line. - # - # * `stream` The Stream to read from. - # * `onLines` The callback to call with each line of data. - # * `onDone` The callback to call when the stream has closed. - bufferStream: (stream, onLines, onDone) -> - stream.setEncoding('utf8') - buffered = '' - - stream.on 'data', (data) => - return if @killed - buffered += data - lastNewlineIndex = buffered.lastIndexOf('\n') - if lastNewlineIndex isnt -1 - onLines(buffered.substring(0, lastNewlineIndex + 1)) - buffered = buffered.substring(lastNewlineIndex + 1) - - stream.on 'close', => - return if @killed - onLines(buffered) if buffered.length > 0 - onDone() - - # Kill all child processes of the spawned cmd.exe process on Windows. - # - # This is required since killing the cmd.exe does not terminate child - # processes. - killOnWindows: -> - return unless @process? - - parentPid = @process.pid - cmd = 'wmic' - args = [ - 'process' - 'where' - "(ParentProcessId=#{parentPid})" - 'get' - 'processid' - ] - - try - wmicProcess = ChildProcess.spawn(cmd, args) - catch spawnError - @killProcess() - return - - wmicProcess.on 'error', -> # ignore errors - output = '' - wmicProcess.stdout.on 'data', (data) -> output += data - wmicProcess.stdout.on 'close', => - pidsToKill = output.split(/\s+/) - .filter (pid) -> /^\d+$/.test(pid) - .map (pid) -> parseInt(pid) - .filter (pid) -> pid isnt parentPid and 0 < pid < Infinity - - for pid in pidsToKill - try - process.kill(pid) - @killProcess() - - killProcess: -> - @process?.kill() - @process = null - - isExplorerCommand: (command) -> - if command is 'explorer.exe' or command is 'explorer' - true - else if process.env.SystemRoot - command is path.join(process.env.SystemRoot, 'explorer.exe') or command is path.join(process.env.SystemRoot, 'explorer') - else - false - - getCmdPath: -> - if process.env.comspec - process.env.comspec - else if process.env.SystemRoot - path.join(process.env.SystemRoot, 'System32', 'cmd.exe') - else - 'cmd.exe' - - # Public: Terminate the process. - kill: -> - return if @killed - - @killed = true - if process.platform is 'win32' - @killOnWindows() - else - @killProcess() - - undefined - - spawn: (command, args, options) -> - try - @process = ChildProcess.spawn(command, args, options) - catch spawnError - setTimeout((=> @handleError(spawnError)), 0) - - handleEvents: (stdout, stderr, exit) -> - return unless @process? - - stdoutClosed = true - stderrClosed = true - processExited = true - exitCode = 0 - triggerExitCallback = -> - return if @killed - if stdoutClosed and stderrClosed and processExited - exit?(exitCode) - - if stdout - stdoutClosed = false - @bufferStream @process.stdout, stdout, -> - stdoutClosed = true - triggerExitCallback() - - if stderr - stderrClosed = false - @bufferStream @process.stderr, stderr, -> - stderrClosed = true - triggerExitCallback() - - if exit - processExited = false - @process.on 'exit', (code) -> - exitCode = code - processExited = true - triggerExitCallback() - - @process.on 'error', (error) => @handleError(error) - return - - handleError: (error) -> - handled = false - handle = -> handled = true - - @emitter.emit 'will-throw-error', {error, handle} - - if error.code is 'ENOENT' and error.syscall.indexOf('spawn') is 0 - error = new Error("Failed to spawn command `#{@command}`. Make sure `#{@command}` is installed and on your PATH", error.path) - error.name = 'BufferedProcessError' - - throw error unless handled diff --git a/packages/client-app/src/color.coffee b/packages/client-app/src/color.coffee deleted file mode 100644 index 997efc978..000000000 --- a/packages/client-app/src/color.coffee +++ /dev/null @@ -1,89 +0,0 @@ -_ = require 'underscore' -ParsedColor = null - -# Essential: A simple color class returned from {Config::get} when the value -# at the key path is of type 'color'. -module.exports = -class Color - # Essential: Parse a {String} or {Object} into a {Color}. - # - # * `value` A {String} such as `'white'`, `#ff00ff`, or - # `'rgba(255, 15, 60, .75)'` or an {Object} with `red`, `green`, `blue`, - # and `alpha` properties. - # - # Returns a {Color} or `null` if it cannot be parsed. - @parse: (value) -> - return null if _.isArray(value) or _.isFunction(value) - return null unless _.isObject(value) or _.isString(value) - - ParsedColor ?= require 'color' - - try - parsedColor = new ParsedColor(value) - catch error - return null - - new Color(parsedColor.red(), parsedColor.green(), parsedColor.blue(), parsedColor.alpha()) - - constructor: (red, green, blue, alpha) -> - Object.defineProperties this, - red: - set: (newRed) -> red = parseColor(newRed) - get: -> red - enumerable: true - configurable: false - green: - set: (newGreen) -> green = parseColor(newGreen) - get: -> green - enumerable: true - configurable: false - blue: - set: (newBlue) -> blue = parseColor(newBlue) - get: -> blue - enumerable: true - configurable: false - alpha: - set: (newAlpha) -> alpha = parseAlpha(newAlpha) - get: -> alpha - enumerable: true - configurable: false - - @red = red - @green = green - @blue = blue - @alpha = alpha - - # Essential: Returns a {String} in the form `'#abcdef'`. - toHexString: -> - "##{numberToHexString(@red)}#{numberToHexString(@green)}#{numberToHexString(@blue)}" - - # Essential: Returns a {String} in the form `'rgba(25, 50, 75, .9)'`. - toRGBAString: -> - "rgba(#{@red}, #{@green}, #{@blue}, #{@alpha})" - - isEqual: (color) -> - return true if this is color - color = Color.parse(color) unless color instanceof Color - return false unless color? - color.red is @red and color.blue is @blue and color.green is @green and color.alpha is @alpha - - clone: -> new Color(@red, @green, @blue, @alpha) - -parseColor = (color) -> - color = parseInt(color) - color = 0 if isNaN(color) - color = Math.max(color, 0) - color = Math.min(color, 255) - color - -parseAlpha = (alpha) -> - alpha = parseFloat(alpha) - alpha = 1 if isNaN(alpha) - alpha = Math.max(alpha, 0) - alpha = Math.min(alpha, 1) - alpha - -numberToHexString = (number) -> - hex = number.toString(16) - hex = "0#{hex}" if number < 10 - hex diff --git a/packages/client-app/src/config.coffee b/packages/client-app/src/config.coffee index 03629bb75..48e5931b0 100644 --- a/packages/client-app/src/config.coffee +++ b/packages/client-app/src/config.coffee @@ -5,8 +5,6 @@ fs = require 'fs-plus' EmitterMixin = require('emissary').Emitter {CompositeDisposable, Disposable, Emitter} = require 'event-kit' -Color = require './color' - if process.type is 'renderer' app = remote.getGlobal('application') webContentsId = remote.getCurrentWebContents().getId() @@ -218,21 +216,6 @@ else # maximum: 11.5 # ``` # -# #### color -# -# Values will be coerced into a {Color} with `red`, `green`, `blue`, and `alpha` -# properties that all have numeric values. `red`, `green`, `blue` will be in -# the range 0 to 255 and `value` will be in the range 0 to 1. Values can be any -# valid CSS color format such as `#abc`, `#abcdef`, `white`, -# `rgb(50, 100, 150)`, and `rgba(25, 75, 125, .75)`. -# -# ```coffee -# config: -# someSetting: -# type: 'color' -# default: 'white' -# ``` -# # ### Other Supported Keys # # #### enum @@ -593,9 +576,7 @@ class Config @_logError("Failed to set keypath to default: #{keyPath} = #{JSON.stringify(defaults)}", e) deepClone: (object) -> - if object instanceof Color - object.clone() - else if _.isArray(object) + if _.isArray(object) object.map (value) => @deepClone(value) else if isPlainObject(object) _.mapObject object, (value) => @deepClone(value) @@ -725,13 +706,6 @@ Config.addSchemaEnforcers else value - 'color': - coerce: (keyPath, value, schema) -> - color = Color.parse(value) - unless color? - throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} cannot be coerced into a color") - color - '*': coerceMinimumAndMaximum: (keyPath, value, schema) -> return value unless typeof value is 'number' @@ -752,7 +726,7 @@ Config.addSchemaEnforcers throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} is not one of #{JSON.stringify(possibleValues)}") isPlainObject = (value) -> - _.isObject(value) and not _.isArray(value) and not _.isFunction(value) and not _.isString(value) and not (value instanceof Color) + _.isObject(value) and not _.isArray(value) and not _.isFunction(value) and not _.isString(value) splitKeyPath = (keyPath) -> return [] unless keyPath? diff --git a/packages/client-app/src/secondary-window-bootstrap.es6 b/packages/client-app/src/secondary-window-bootstrap.es6 index f0f5ab749..ab9b8cfb1 100644 --- a/packages/client-app/src/secondary-window-bootstrap.es6 +++ b/packages/client-app/src/secondary-window-bootstrap.es6 @@ -14,8 +14,6 @@ // Extend the standard promise class a bit import './promise-extensions'; -import './window'; - import NylasEnvConstructor from './nylas-env'; window.NylasEnv = window.atom = NylasEnvConstructor.loadOrCreate(); diff --git a/packages/client-app/src/task-bootstrap.coffee b/packages/client-app/src/task-bootstrap.coffee deleted file mode 100644 index 6690e2270..000000000 --- a/packages/client-app/src/task-bootstrap.coffee +++ /dev/null @@ -1,48 +0,0 @@ -{userAgent, taskPath} = process.env -handler = null - -setupGlobals = -> - global.attachEvent = -> - console = - warn: -> emit 'task:warn', arguments... - log: -> emit 'task:log', arguments... - error: -> emit 'task:error', arguments... - trace: -> - global.__defineGetter__ 'console', -> console - - global.document = - createElement: -> - setAttribute: -> - getElementsByTagName: -> [] - appendChild: -> - documentElement: - insertBefore: -> - removeChild: -> - getElementById: -> {} - createComment: -> {} - createDocumentFragment: -> {} - - global.emit = (event, args...) -> - process.send({event, args}) - global.navigator = {userAgent} - global.window = global - -handleEvents = -> - process.on 'unhandledRejection', (reason, promise) -> - console.error(reason.stack, promise) - process.on 'uncaughtException', (error) -> - console.error(error.message, error.stack) - process.on 'message', ({event, args}={}) -> - return unless event is 'start' - - isAsync = false - async = -> - isAsync = true - (result) -> - emit('task:completed', result) - result = handler.bind({async})(args...) - emit('task:completed', result) unless isAsync - -setupGlobals() -handleEvents() -handler = require(taskPath) diff --git a/packages/client-app/src/task.coffee b/packages/client-app/src/task.coffee deleted file mode 100644 index 1ebfe3753..000000000 --- a/packages/client-app/src/task.coffee +++ /dev/null @@ -1,167 +0,0 @@ -_ = require 'underscore' -ChildProcess = require 'child_process' -{Emitter} = require 'event-kit' - -# Extended: Run a node script in a separate process. -# -# Used by the fuzzy-finder and [find in project](https://github.com/atom/atom/blob/master/src/scan-handler.coffee). -# -# For a real-world example, see the [scan-handler](https://github.com/atom/atom/blob/master/src/scan-handler.coffee) -# and the [instantiation of the task](https://github.com/atom/atom/blob/4a20f13162f65afc816b512ad7201e528c3443d7/src/project.coffee#L245). -# -# ## Examples -# -# In your package code: -# -# ```coffee -# {Task} = require './task' -# -# task = Task.once '/path/to/task-file.coffee', parameter1, parameter2, -> -# console.log 'task has finished' -# -# task.on 'some-event-from-the-task', (data) => -# console.log data.someString # prints 'yep this is it' -# ``` -# -# In `'/path/to/task-file.coffee'`: -# -# ```coffee -# module.exports = (parameter1, parameter2) -> -# # Indicates that this task will be async. -# # Call the `callback` to finish the task -# callback = @async() -# -# emit('some-event-from-the-task', {someString: 'yep this is it'}) -# -# callback() -# ``` -module.exports = -class Task - # Public: A helper method to easily launch and run a task once. - # - # * `taskPath` The {String} path to the CoffeeScript/JavaScript file which - # exports a single {Function} to execute. - # * `args` The arguments to pass to the exported function. - # - # Returns the created {Task}. - @once: (taskPath, args...) -> - task = new Task(taskPath) - task.once 'task:completed', -> task.terminate() - task.start(args...) - task - - # Called upon task completion. - # - # It receives the same arguments that were passed to the task. - # - # If subclassed, this is intended to be overridden. However if {::start} - # receives a completion callback, this is overridden. - callback: null - - # Public: Creates a task. You should probably use {.once} - # - # * `taskPath` The {String} path to the CoffeeScript/JavaScript file that - # exports a single {Function} to execute. - constructor: (taskPath) -> - @emitter = new Emitter - - compileCacheRequire = "require('#{require.resolve('./compile-cache')}')" - compileCachePath = require('./compile-cache').getCacheDirectory() - taskBootstrapRequire = "require('#{require.resolve('./task-bootstrap')}');" - bootstrap = """ - #{compileCacheRequire}.setCacheDirectory('#{compileCachePath}'); - #{taskBootstrapRequire} - """ - bootstrap = bootstrap.replace(/\\/g, "\\\\") - - taskPath = require.resolve(taskPath) - taskPath = taskPath.replace(/\\/g, "\\\\") - - env = Object.assign({}, process.env, {taskPath, userAgent: 'NylasMail'}) - @childProcess = ChildProcess.fork '--eval', [bootstrap], {env, silent: true} - - @on "task:log", -> console.log(arguments...) - @on "task:warn", -> console.warn(arguments...) - @on "task:error", -> console.error(arguments...) - @on "task:completed", (args...) => @callback?(args...) - - @handleEvents() - - # Routes messages from the child to the appropriate event. - handleEvents: -> - @childProcess.removeAllListeners() - @childProcess.on 'message', ({event, args}) => - @emitter.emit(event, args) if @childProcess? - - # Catch the errors that happened before task-bootstrap. - if @childProcess.stdout? - @childProcess.stdout.removeAllListeners() - @childProcess.stdout.on 'data', (data) -> console.log data.toString() - - if @childProcess.stderr? - @childProcess.stderr.removeAllListeners() - @childProcess.stderr.on 'data', (data) -> console.error data.toString() - - # Public: Starts the task. - # - # Throws an error if this task has already been terminated or if sending a - # message to the child process fails. - # - # * `args` The arguments to pass to the function exported by this task's script. - # * `callback` (optional) A {Function} to call when the task completes. - start: (args..., callback) -> - throw new Error('Cannot start terminated process') unless @childProcess? - - @handleEvents() - if _.isFunction(callback) - @callback = callback - else - args.push(callback) - @send({event: 'start', args}) - undefined - - # Public: Send message to the task. - # - # Throws an error if this task has already been terminated or if sending a - # message to the child process fails. - # - # * `message` The message to send to the task. - send: (message) -> - if @childProcess? - @childProcess.send(message) - else - throw new Error('Cannot send message to terminated process') - undefined - - # Public: Call a function when an event is emitted by the child process - # - # * `eventName` The {String} name of the event to handle. - # * `callback` The {Function} to call when the event is emitted. - # - # Returns a {Disposable} that can be used to stop listening for the event. - on: (eventName, callback) -> @emitter.on eventName, (args) -> callback(args...) - - once: (eventName, callback) -> - disposable = @on eventName, (args...) -> - disposable.dispose() - callback(args...) - - # Public: Forcefully stop the running task. - # - # No more events are emitted once this method is called. - terminate: -> - return false unless @childProcess? - - @childProcess.removeAllListeners() - @childProcess.stdout?.removeAllListeners() - @childProcess.stderr?.removeAllListeners() - @childProcess.kill() - @childProcess = null - - true - - cancel: -> - didForcefullyTerminate = @terminate() - if didForcefullyTerminate - @emitter.emit('task:canceled') - didForcefullyTerminate diff --git a/packages/client-app/src/window-bootstrap.es6 b/packages/client-app/src/window-bootstrap.es6 index 203422842..5c03f8fbc 100644 --- a/packages/client-app/src/window-bootstrap.es6 +++ b/packages/client-app/src/window-bootstrap.es6 @@ -3,9 +3,6 @@ // Extend the standard promise class a bit import './promise-extensions'; -// Like sands through the hourglass, so are the days of our lives. -import './window'; - import NylasEnvConstructor from './nylas-env'; window.NylasEnv = NylasEnvConstructor.loadOrCreate(); NylasEnv.initialize(); diff --git a/packages/client-app/src/window.coffee b/packages/client-app/src/window.coffee deleted file mode 100644 index 9554218ca..000000000 --- a/packages/client-app/src/window.coffee +++ /dev/null @@ -1,27 +0,0 @@ -# Public: Measure how long a function takes to run. -# -# description - A {String} description that will be logged to the console when -# the function completes. -# fn - A {Function} to measure the duration of. -# -# Returns the value returned by the given function. -window.measure = (description, fn) -> - start = Date.now() - value = fn() - result = Date.now() - start - console.log description, result - value - -# Public: Create a dev tools profile for a function. -# -# description - A {String} description that will be available in the Profiles -# tab of the dev tools. -# fn - A {Function} to profile. -# -# Returns the value returned by the given function. -window.profile = (description, fn) -> - measure description, -> - console.profile(description) - value = fn() - console.profileEnd(description) - value