<commit_before>'use strict'

const debug = require('debug')('0x: ticks-to-tree')
const { join } = require('path')
module.exports = ticksToTree

function ticksToTree (ticks, mapFrames, inlined) {
  const merged = {
    name: 'all stacks',
    value: 0,
    top: 0,
    children: []
  }
  const unmerged = {
    name: 'all stacks',
    value: 0,
    top: 0,
    children: []
  }
  const inlineInfo = typeof inlined === 'object'

  ticks.forEach((stack) => {
    stack = removeInstrumentationFrames(stack)
    if (typeof mapFrames === 'function') stack = mapFrames(stack)
    if (!stack) return
    stack = stack.map(({name, kind, type}, ix) => {
      name = name.replace(/ (:[0-9]+:[0-9]+)/, (_, loc) => ` [eval]${loc}`)
      // 0 no info
      // 1 unoptimized
      // 2 optimized
      // 3 inlinable unoptimized
      // 4 inlinable optimized
      var S = 0

      if (type === 'JS') {
        if (name[0] === ' ') name = '(anonymous)' + name
        if (inlineInfo === true) {
          const [ key ] = name.split(':')
          const info = inlined[key]
          if (info !== undefined) {
            const sameFn = info.length === 1 || info.every(({pos}, ix) => {
              const prev = info[ix - 1]
              if (!prev) return true
              return prev.pos === pos
            })
            if (sameFn === false) {
              // todo: resolve this edge case as follows:
              // collect stack frames with same key (fn + file) but different line:col
              // if the amount of stacks frames with same key matches size of `info`
              // then they can all be marked as inlinable
              // if not then,
              // sort functions in inlined by position
              // collect stack frames with same key (fn + file) but sort
              // them by line:col (const [ key, line, col ] = name.split(':'))
              // -- any stack frames that exceed length of info are *not* inlinable
              // the rest are
              debug(`multiple functions name collision in file skipping ${key}`)
            } else {
              const [ lookup ] = stack[ix - 1] ? (stack[ix - 1].name.split(':')) : []
              const callerMatch = inlined[key].find(({caller}) => caller.key === lookup)
              if (callerMatch) S += 2
            }
          }
        }
        if (kind === 'Unopt') S += 1
        if (kind === 'Opt') S += 2
      }

      if (type && type !== 'JS') name += ' [' + type + (kind ? ':' + kind : '') + ']'
      return {S, name, value: 0, top: 0}
    })

    stack = labelInitFrames(stack)

    addToMergedTree(stack.map(({S, name, value, top}) => ({S, name, value, top})))
    // mutate original (save another loop over stack + extra objects)
    addToUnmergedTree(stack)
  })

  function addToMergedTree (stack) {
    var lastFrame = null
    stack.forEach((frame, ix) => {
      // TODO need an elegant way to place inlined frames into *other* stacks
      // where the function has disappeared – for now we just remove inlined
      // stacks from merged graphs
      if (frame.S > 2) return // skip inlined
      if (ix > 0) lastFrame.children = lastFrame.children || []
      const children = (ix === 0) ? merged.children : lastFrame.children
      const child = children.find(({name}) => name === frame.name)

      if (child === undefined) children.push(frame)
      else frame = child

      if (ix === stack.length - 1) frame.top++
      if (ix === 0) merged.value += 1
      frame.value++

      lastFrame = frame
    })
  }

  function addToUnmergedTree (stack) {
    var lastFrame = null
    stack.forEach((frame, ix) => {
      // TODO need an elegant way to place inlined frames into *other* stacks
      // where the function has disappeared – for now we just remove inlined
      // stacks from merged graphs
      // adding children here means we avoid unnecessary arrays
      if (ix > 0) lastFrame.children = lastFrame.children || []
      const children = (ix === 0) ? unmerged.children : lastFrame.children
      const child = children.find(({fn, S}) => {
        return fn === frame.name && S === frame.S
      })

      if (child === undefined) {
        frame.fn = frame.name
        if (frame.S === 1) frame.name = '~' + frame.name
        if (frame.S === 2) frame.name = '*' + frame.name
        if (frame.S === 3) frame.name = '~' + frame.name + ' [INLINABLE]'
        if (frame.S === 4) frame.name = '*' + frame.name + ' [INLINABLE]'
        children.push(frame)
      } else frame = child

      if (ix === stack.length - 1) frame.top++
      if (ix === 0) unmerged.value += 1
      frame.value++

      lastFrame = frame
    })
  }

  return { merged, unmerged }
}

function labelInitFrames (frames) {
  const startupBootstrapNodeIndex = frames.findIndex(({name}, ix) => {
    if (frames[ix + 1] && /Module.runMain module\.js/.test(frames[ix + 1].name)) return false
    return /startup bootstrap_node\.js/.test(name)
  })

  if (startupBootstrapNodeIndex !== -1) {
    frames.slice(startupBootstrapNodeIndex + 1).forEach((frame) => {
      if (frame.isInit) return
      frame.name += ' [INIT]'
      frame.isInit = true
    })
  }

  const moduleRunMainIndex = frames.findIndex(({name}, ix) => {
    return /Module.runMain module\.js/.test(name)
  })

  if (moduleRunMainIndex !== -1) {
    frames.slice(moduleRunMainIndex + 1).forEach((frame) => {
      if (frame.isInit) return
      if (/.+ (internal\/)?module\.js/.test(frame.name)) frame.name += '[INIT]'
      frame.isInit = true
    })
  }

  return frames
}

function removeInstrumentationFrames (frames) {
  const preloadDirRx = RegExp(join(__dirname, 'preload'))
  return frames.filter((frame) => preloadDirRx.test(frame.name) === false)
}<commit_msg>researching inlinable edge cases, update comment<commit_after>'use strict'

const debug = require('debug')('0x: ticks-to-tree')
const { join } = require('path')
module.exports = ticksToTree

function ticksToTree (ticks, mapFrames, inlined) {
  const merged = {
    name: 'all stacks',
    value: 0,
    top: 0,
    children: []
  }
  const unmerged = {
    name: 'all stacks',
    value: 0,
    top: 0,
    children: []
  }
  const inlineInfo = typeof inlined === 'object'

  ticks.forEach((stack) => {
    stack = removeInstrumentationFrames(stack)
    if (typeof mapFrames === 'function') stack = mapFrames(stack)
    if (!stack) return
    stack = stack.map(({name, kind, type}, ix) => {
      name = name.replace(/ (:[0-9]+:[0-9]+)/, (_, loc) => ` [eval]${loc}`)
      // 0 no info
      // 1 unoptimized
      // 2 optimized
      // 3 inlinable unoptimized
      // 4 inlinable optimized
      var S = 0

      if (type === 'JS') {
        if (name[0] === ' ') name = '(anonymous)' + name
        if (inlineInfo === true) {
          const [ key ] = name.split(':')
          const info = inlined[key]
          if (info !== undefined) {
            const sameFn = info.length === 1 || info.every(({pos}, ix) => {
              const prev = info[ix - 1]
              if (!prev) return true
              return prev.pos === pos
            })
            if (sameFn === false) {
              // todo: need examples of this occurring in the wild
              // resolve this edge case as follows (if we can actually reproduce it):
              // collect stack frames with same key (fn + file) but different line:col
              // if the amount of stacks frames with same key matches size of `info`
              // then they can all be marked as inlinable
              // if not then,
              // sort functions in inlined by position
              // collect stack frames with same key (fn + file) but sort
              // them by line:col (const [ key, line, col ] = name.split(':'))
              // -- any stack frames that exceed length of info are *not* inlinable
              // the rest are
              debug(`multiple functions name collision in file skipping ${key}`)
            } else {
              const [ lookup ] = stack[ix - 1] ? (stack[ix - 1].name.split(':')) : []
              const callerMatch = inlined[key].find(({caller}) => caller.key === lookup)
              if (callerMatch) S += 2
            }
          }
        }
        if (kind === 'Unopt') S += 1
        if (kind === 'Opt') S += 2
      }

      if (type && type !== 'JS') name += ' [' + type + (kind ? ':' + kind : '') + ']'
      return {S, name, value: 0, top: 0}
    })

    stack = labelInitFrames(stack)

    addToMergedTree(stack.map(({S, name, value, top}) => ({S, name, value, top})))
    // mutate original (save another loop over stack + extra objects)
    addToUnmergedTree(stack)
  })

  function addToMergedTree (stack) {
    var lastFrame = null
    stack.forEach((frame, ix) => {
      // TODO need an elegant way to place inlined frames into *other* stacks
      // where the function has disappeared – for now we just remove inlined
      // stacks from merged graphs
      if (frame.S > 2) return // skip inlined
      if (ix > 0) lastFrame.children = lastFrame.children || []
      const children = (ix === 0) ? merged.children : lastFrame.children
      const child = children.find(({name}) => name === frame.name)

      if (child === undefined) children.push(frame)
      else frame = child

      if (ix === stack.length - 1) frame.top++
      if (ix === 0) merged.value += 1
      frame.value++

      lastFrame = frame
    })
  }

  function addToUnmergedTree (stack) {
    var lastFrame = null
    stack.forEach((frame, ix) => {
      // TODO need an elegant way to place inlined frames into *other* stacks
      // where the function has disappeared – for now we just remove inlined
      // stacks from merged graphs
      // adding children here means we avoid unnecessary arrays
      if (ix > 0) lastFrame.children = lastFrame.children || []
      const children = (ix === 0) ? unmerged.children : lastFrame.children
      const child = children.find(({fn, S}) => {
        return fn === frame.name && S === frame.S
      })

      if (child === undefined) {
        frame.fn = frame.name
        if (frame.S === 1) frame.name = '~' + frame.name
        if (frame.S === 2) frame.name = '*' + frame.name
        if (frame.S === 3) frame.name = '~' + frame.name + ' [INLINABLE]'
        if (frame.S === 4) frame.name = '*' + frame.name + ' [INLINABLE]'
        children.push(frame)
      } else frame = child

      if (ix === stack.length - 1) frame.top++
      if (ix === 0) unmerged.value += 1
      frame.value++

      lastFrame = frame
    })
  }

  return { merged, unmerged }
}

function labelInitFrames (frames) {
  const startupBootstrapNodeIndex = frames.findIndex(({name}, ix) => {
    if (frames[ix + 1] && /Module.runMain module\.js/.test(frames[ix + 1].name)) return false
    return /startup bootstrap_node\.js/.test(name)
  })

  if (startupBootstrapNodeIndex !== -1) {
    frames.slice(startupBootstrapNodeIndex + 1).forEach((frame) => {
      if (frame.isInit) return
      frame.name += ' [INIT]'
      frame.isInit = true
    })
  }

  const moduleRunMainIndex = frames.findIndex(({name}, ix) => {
    return /Module.runMain module\.js/.test(name)
  })

  if (moduleRunMainIndex !== -1) {
    frames.slice(moduleRunMainIndex + 1).forEach((frame) => {
      if (frame.isInit) return
      if (/.+ (internal\/)?module\.js/.test(frame.name)) frame.name += '[INIT]'
      frame.isInit = true
    })
  }

  return frames
}

function removeInstrumentationFrames (frames) {
  const preloadDirRx = RegExp(join(__dirname, 'preload'))
  return frames.filter((frame) => preloadDirRx.test(frame.name) === false)
}<|endoftext|>


<commit_before>const {CompositeDisposable, Emitter} = require('atom');
const rafDebounce = require('./raf-debounce');

module.exports = class ToolBarView {
  constructor () {
    this.element = document.createElement('div');
    this.element.classList.add('tool-bar');
    this.items = [];
    this.emitter = new Emitter();
    this.subscriptions = new CompositeDisposable();

    this.subscriptions.add(
      atom.commands.add('atom-workspace', 'tool-bar:toggle', () => {
        this.toggle();
      }),
      atom.commands.add('atom-workspace', 'tool-bar:position-top', () => {
        this.updatePosition('Top');
      }),
      atom.commands.add('atom-workspace', 'tool-bar:position-right', () => {
        this.updatePosition('Right');
      }),
      atom.commands.add('atom-workspace', 'tool-bar:position-bottom', () => {
        this.updatePosition('Bottom');
      }),
      atom.commands.add('atom-workspace', 'tool-bar:position-left', () => {
        this.updatePosition('Left');
      }),
      atom.config.observe('tool-bar.iconSize', newValue => {
        this.updateSize(newValue);
      }),
      atom.config.onDidChange('tool-bar.position', () => {
        if (atom.config.get('tool-bar.visible')) {
          this.show();
        }
      }),
      atom.config.onDidChange('tool-bar.visible', ({newValue}) => {
        if (newValue) {
          this.show();
        } else {
          this.hide();
        }
      }),
      atom.config.onDidChange('tool-bar.fullWidth', () => {
        if (atom.config.get('tool-bar.visible')) {
          this.show();
        }
      })
    );

    if (atom.config.get('tool-bar.visible')) {
      this.show();
    }

    this.drawGutter = rafDebounce(this.drawGutter.bind(this));
    this.subscriptions.add(this.drawGutter);

    this.element.addEventListener('scroll', this.drawGutter);
    window.addEventListener('resize', this.drawGutter);
  }

  addItem (newItem) {
    newItem.priority = this.calculatePriority(newItem);

    if (atom.inDevMode()) {
      newItem.element.dataset.group = newItem.group;
      newItem.element.dataset.priority = newItem.priority;
    }

    let index = this.items.findIndex(existingItem =>
      (existingItem.priority > newItem.priority));
    if (index === -1) {
      index = this.items.length;
    }
    const nextItem = this.items[index];

    this.items.splice(index, 0, newItem);

    this.element.insertBefore(
      newItem.element,
      nextItem ? nextItem.element : null
    );

    this.drawGutter();

    return nextItem;
  }

  removeItem (item) {
    item.destroy();
    this.items.splice(this.items.indexOf(item), 1);
    this.drawGutter();
  }

  destroy () {
    this.items.forEach(item => item.destroy());
    this.items = null;

    this.subscriptions.dispose();
    this.subscriptions = null;

    this.hide();
    this.element.removeEventListener('scroll', this.drawGutter);
    this.element = null;

    window.removeEventListener('resize', this.drawGutter);

    this.emitter.emit('did-destroy');
    this.emitter.dispose();
    this.emitter = null;
  }

  calculatePriority (item) {
    if (!isNaN(item.priority)) {
      return item.priority;
    }
    const lastItem = this.items.filter(i => i.group !== item.group).pop();
    return lastItem && !isNaN(lastItem.priority)
      ? lastItem.priority + 1
      : 50;
  }

  updateSize (size) {
    this.element.classList.remove(
      'tool-bar-12px',
      'tool-bar-14px',
      'tool-bar-16px',
      'tool-bar-18px',
      'tool-bar-21px',
      'tool-bar-24px',
      'tool-bar-28px',
      'tool-bar-32px'
    );
    this.element.classList.add(`tool-bar-${size}`);
  }

  updatePosition (position) {
    this.element.classList.remove(
      'tool-bar-top',
      'tool-bar-right',
      'tool-bar-bottom',
      'tool-bar-left',
      'tool-bar-horizontal',
      'tool-bar-vertical'
    );

    const fullWidth = atom.config.get('tool-bar.fullWidth');

    switch (position) {
      case 'Top':
        this.panel = fullWidth
          ? atom.workspace.addHeaderPanel({item: this.element})
          : atom.workspace.addTopPanel({item: this.element});
        break;
      case 'Right':
        this.panel = atom.workspace.addRightPanel({item: this.element});
        break;
      case 'Bottom':
        this.panel = fullWidth
          ? atom.workspace.addFooterPanel({item: this.element})
          : atom.workspace.addBottomPanel({item: this.element});
        break;
      case 'Left':
        this.panel = atom.workspace.addLeftPanel({item: this.element, priority: 50});
        break;
    }

    const classNames = [`tool-bar-${position.toLowerCase()}`];
    if (position === 'Top' || position === 'Bottom') {
      classNames.push('tool-bar-horizontal');
    } else {
      classNames.push('tool-bar-vertical');
    }
    this.element.classList.add(...classNames);

    this.drawGutter();
    atom.config.set('tool-bar.position', position);
  }

  drawGutter () {
    this.element.classList.remove('gutter-top', 'gutter-bottom');

    const visibleHeight = this.element.offsetHeight;
    const scrollHeight = this.element.scrollHeight;
    const hiddenHeight = scrollHeight - visibleHeight;

    if (visibleHeight < scrollHeight) {
      if (this.element.scrollTop > 0) {
        this.element.classList.add('gutter-top');
      }
      if (this.element.scrollTop < hiddenHeight) {
        this.element.classList.add('gutter-bottom');
      }
    }
  }

  hide () {
    if (this.panel != null) {
      if (this.element.parentNode) {
        this.element.parentNode.removeChild(this.element);
      }
      this.panel.destroy();
      this.panel = null;
    }
  }

  show () {
    this.hide();
    this.updatePosition(atom.config.get('tool-bar.position'));
    this.updateSize(atom.config.get('tool-bar.iconSize'));
  }

  toggle () {
    atom.config.set('tool-bar.visible', !atom.config.get('tool-bar.visible'));
  }
};
<commit_msg>Add refresh method to ToolBarView<commit_after>const {CompositeDisposable, Emitter} = require('atom');
const rafDebounce = require('./raf-debounce');

module.exports = class ToolBarView {
  constructor () {
    this.element = document.createElement('div');
    this.element.classList.add('tool-bar');
    this.items = [];
    this.emitter = new Emitter();
    this.subscriptions = new CompositeDisposable();

    this.subscriptions.add(
      atom.commands.add('atom-workspace', 'tool-bar:toggle', () => {
        this.toggle();
      }),
      atom.commands.add('atom-workspace', 'tool-bar:position-top', () => {
        this.updatePosition('Top');
      }),
      atom.commands.add('atom-workspace', 'tool-bar:position-right', () => {
        this.updatePosition('Right');
      }),
      atom.commands.add('atom-workspace', 'tool-bar:position-bottom', () => {
        this.updatePosition('Bottom');
      }),
      atom.commands.add('atom-workspace', 'tool-bar:position-left', () => {
        this.updatePosition('Left');
      }),
      atom.config.observe('tool-bar.iconSize', newValue => {
        this.updateSize(newValue);
      }),
      atom.config.onDidChange('tool-bar.position', () => {
        this.refresh();
      }),
      atom.config.onDidChange('tool-bar.visible', ({newValue}) => {
        if (newValue) {
          this.show();
        } else {
          this.hide();
        }
      }),
      atom.config.onDidChange('tool-bar.fullWidth', () => {
        this.refresh();
      })
    );

    if (atom.config.get('tool-bar.visible')) {
      this.show();
    }

    this.drawGutter = rafDebounce(this.drawGutter.bind(this));
    this.subscriptions.add(this.drawGutter);

    this.element.addEventListener('scroll', this.drawGutter);
    window.addEventListener('resize', this.drawGutter);
  }

  addItem (newItem) {
    newItem.priority = this.calculatePriority(newItem);

    if (atom.inDevMode()) {
      newItem.element.dataset.group = newItem.group;
      newItem.element.dataset.priority = newItem.priority;
    }

    let index = this.items.findIndex(existingItem =>
      (existingItem.priority > newItem.priority));
    if (index === -1) {
      index = this.items.length;
    }
    const nextItem = this.items[index];

    this.items.splice(index, 0, newItem);

    this.element.insertBefore(
      newItem.element,
      nextItem ? nextItem.element : null
    );

    this.drawGutter();

    return nextItem;
  }

  removeItem (item) {
    item.destroy();
    this.items.splice(this.items.indexOf(item), 1);
    this.drawGutter();
  }

  destroy () {
    this.items.forEach(item => item.destroy());
    this.items = null;

    this.subscriptions.dispose();
    this.subscriptions = null;

    this.hide();
    this.element.removeEventListener('scroll', this.drawGutter);
    this.element = null;

    window.removeEventListener('resize', this.drawGutter);

    this.emitter.emit('did-destroy');
    this.emitter.dispose();
    this.emitter = null;
  }

  calculatePriority (item) {
    if (!isNaN(item.priority)) {
      return item.priority;
    }
    const lastItem = this.items.filter(i => i.group !== item.group).pop();
    return lastItem && !isNaN(lastItem.priority)
      ? lastItem.priority + 1
      : 50;
  }

  updateSize (size) {
    this.element.classList.remove(
      'tool-bar-12px',
      'tool-bar-14px',
      'tool-bar-16px',
      'tool-bar-18px',
      'tool-bar-21px',
      'tool-bar-24px',
      'tool-bar-28px',
      'tool-bar-32px'
    );
    this.element.classList.add(`tool-bar-${size}`);
  }

  updatePosition (position) {
    this.element.classList.remove(
      'tool-bar-top',
      'tool-bar-right',
      'tool-bar-bottom',
      'tool-bar-left',
      'tool-bar-horizontal',
      'tool-bar-vertical'
    );

    const fullWidth = atom.config.get('tool-bar.fullWidth');

    switch (position) {
      case 'Top':
        this.panel = fullWidth
          ? atom.workspace.addHeaderPanel({item: this.element})
          : atom.workspace.addTopPanel({item: this.element});
        break;
      case 'Right':
        this.panel = atom.workspace.addRightPanel({item: this.element});
        break;
      case 'Bottom':
        this.panel = fullWidth
          ? atom.workspace.addFooterPanel({item: this.element})
          : atom.workspace.addBottomPanel({item: this.element});
        break;
      case 'Left':
        this.panel = atom.workspace.addLeftPanel({item: this.element, priority: 50});
        break;
    }

    const classNames = [`tool-bar-${position.toLowerCase()}`];
    if (position === 'Top' || position === 'Bottom') {
      classNames.push('tool-bar-horizontal');
    } else {
      classNames.push('tool-bar-vertical');
    }
    this.element.classList.add(...classNames);

    this.drawGutter();
    atom.config.set('tool-bar.position', position);
  }

  drawGutter () {
    this.element.classList.remove('gutter-top', 'gutter-bottom');

    const visibleHeight = this.element.offsetHeight;
    const scrollHeight = this.element.scrollHeight;
    const hiddenHeight = scrollHeight - visibleHeight;

    if (visibleHeight < scrollHeight) {
      if (this.element.scrollTop > 0) {
        this.element.classList.add('gutter-top');
      }
      if (this.element.scrollTop < hiddenHeight) {
        this.element.classList.add('gutter-bottom');
      }
    }
  }

  hide () {
    if (this.panel != null) {
      if (this.element.parentNode) {
        this.element.parentNode.removeChild(this.element);
      }
      this.panel.destroy();
      this.panel = null;
    }
  }

  show () {
    this.hide();
    this.updatePosition(atom.config.get('tool-bar.position'));
    this.updateSize(atom.config.get('tool-bar.iconSize'));
  }

  refresh () {
    if (atom.config.get('tool-bar.visible')) {
      this.show();
    }
  }

  toggle () {
    atom.config.set('tool-bar.visible', !atom.config.get('tool-bar.visible'));
  }
};
<|endoftext|>


<commit_before>/**
 *  Module dependencies
 */
var Db = require('mongodb').Db,
  Connection = require('mongodb').Connection,
  Server = require('mongodb').Server; var _ = require('underscore');
var http = require('http');
var events = require('events');
var _ = require('underscore');

/**
 *  Module constants
 */
var LOG_TAG = '  Tweet Updater: '
var DEFAULT_DELAY = 300000;
var NUM_TWEETS = 10;
var MY_TIMELINE = 'http://api.twitter.com/1/statuses/user_timeline.json?include_rts=true&screen_name=diegonetto11';
var O_EMBED_BASE = 'http://api.twitter.com/1/statuses/oembed.json?omit_script=true&id=';
var HOST = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost';
var PORT = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : Connection.DEFAULT_PORT;

/**
 *  Module variables
 */
var timeoutId = 0;
var tweetCollection = {};
var TweetUpdater = new events.EventEmitter();

// Connect to 'diegonetto' database.
console.log(LOG_TAG + 'connecting to database at ' + HOST + ':' + PORT);
var db = new Db('diegonetto', new Server(HOST, PORT, {}));

/**
 * Start the Tweet Updater
 */
exports.start = function(delay, numTweets) {
  console.log(LOG_TAG + 'Starting');
  var delay = delay || DEFAULT_DELAY;
  NUM_TWEETS = numTweets || NUM_TWEETS;

  // Open the database connection
  db.open(function(err, db) {
    console.log(LOG_TAG + 'opened database connection');
    db.collection('tweets', function(err, collection) {
      tweetCollection = collection;
    });
  });

  // Set Timer for pulling down embedded tweets every minute
  timeoutId = setInterval(pullTweets, delay);
};

/**
 * Stop the Tweet Updater
 */
exports.stop = function() {
  console.log(LOG_TAG + 'Stopping');
  clearTimeout(timeoutId);

  // Close the database connection
  console.log(LOG_TAG + 'closing database connection');
  db.close();
}

/**
 * Make a request to the Twitter API for my last NUM_TWEETS
 * and emit the 'tweets' event if a JSON object was successfully
 * parsed from the data when the request ends.
 */
function pullTweets() {
  console.log(LOG_TAG + 'pulling recent Tweets');

  // Make a request to retrieve most recent num Tweets
  http.get(MY_TIMELINE + '&count=' + NUM_TWEETS, function(res) {
    var data = '';
    res.on('data', function(chunk) {
      data += chunk;
    });

    res.on('end', function() {
      var tweets = JSON.parse(data);
      if (tweets) {
        TweetUpdater.emit('tweets', tweets);
      }
    });

    res.on('close', function(err) {
      console.log(err);
    });
  });
};

/**
 * Listen for the 'tweets' event and make a request to the Twitter
 * API for each tweet's oEmbed endpoint. When the request ends,
 * parse the resulting JSON and if valid insert into tweetCollection.
 */
TweetUpdater.on('tweets', function(tweets) {
  // Remove all Tweets from the collection, if any
  tweetCollection.remove({}, function( err, result) {
    _.each(tweets, function(tweet, id, tweets) {
      http.get(O_EMBED_BASE + tweet.id_str, function(res) {
        var data = '';
        res.on('data', function(chunk) {
          data += chunk;
        });

        res.on('end', function() {
          var tweet = JSON.parse(data);
          if (tweet) {
            console.log(LOG_TAG + 'saving tweet');
            tweetCollection.insert(tweet);
          }
        });

        res.on('close', function(err) {
          console.log(err);
        });
      });
    });
  });
});
<commit_msg>Clean up redundant require.<commit_after>/**
 *  Module dependencies
 */
var Db = require('mongodb').Db,
  Connection = require('mongodb').Connection,
  Server = require('mongodb').Server;
var http = require('http');
var events = require('events');
var _ = require('underscore');

/**
 *  Module constants
 */
var LOG_TAG = '  Tweet Updater: '
var DEFAULT_DELAY = 300000;
var NUM_TWEETS = 10;
var MY_TIMELINE = 'http://api.twitter.com/1/statuses/user_timeline.json?include_rts=true&screen_name=diegonetto11';
var O_EMBED_BASE = 'http://api.twitter.com/1/statuses/oembed.json?omit_script=true&id=';
var HOST = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost';
var PORT = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : Connection.DEFAULT_PORT;

/**
 *  Module variables
 */
var timeoutId = 0;
var tweetCollection = {};
var TweetUpdater = new events.EventEmitter();

// Connect to 'diegonetto' database.
console.log(LOG_TAG + 'connecting to database at ' + HOST + ':' + PORT);
var db = new Db('diegonetto', new Server(HOST, PORT, {}));

/**
 * Start the Tweet Updater
 */
exports.start = function(delay, numTweets) {
  console.log(LOG_TAG + 'Starting');
  var delay = delay || DEFAULT_DELAY;
  NUM_TWEETS = numTweets || NUM_TWEETS;

  // Open the database connection
  db.open(function(err, db) {
    console.log(LOG_TAG + 'opened database connection');
    db.collection('tweets', function(err, collection) {
      tweetCollection = collection;
    });
  });

  // Set Timer for pulling down embedded tweets every minute
  timeoutId = setInterval(pullTweets, delay);
};

/**
 * Stop the Tweet Updater
 */
exports.stop = function() {
  console.log(LOG_TAG + 'Stopping');
  clearTimeout(timeoutId);

  // Close the database connection
  console.log(LOG_TAG + 'closing database connection');
  db.close();
}

/**
 * Make a request to the Twitter API for my last NUM_TWEETS
 * and emit the 'tweets' event if a JSON object was successfully
 * parsed from the data when the request ends.
 */
function pullTweets() {
  console.log(LOG_TAG + 'pulling recent Tweets');

  // Make a request to retrieve most recent num Tweets
  http.get(MY_TIMELINE + '&count=' + NUM_TWEETS, function(res) {
    var data = '';
    res.on('data', function(chunk) {
      data += chunk;
    });

    res.on('end', function() {
      var tweets = JSON.parse(data);
      if (tweets) {
        TweetUpdater.emit('tweets', tweets);
      }
    });

    res.on('close', function(err) {
      console.log(err);
    });
  });
};

/**
 * Listen for the 'tweets' event and make a request to the Twitter
 * API for each tweet's oEmbed endpoint. When the request ends,
 * parse the resulting JSON and if valid insert into tweetCollection.
 */
TweetUpdater.on('tweets', function(tweets) {
  // Remove all Tweets from the collection, if any
  tweetCollection.remove({}, function( err, result) {
    _.each(tweets, function(tweet, id, tweets) {
      http.get(O_EMBED_BASE + tweet.id_str, function(res) {
        var data = '';
        res.on('data', function(chunk) {
          data += chunk;
        });

        res.on('end', function() {
          var tweet = JSON.parse(data);
          if (tweet) {
            console.log(LOG_TAG + 'saving tweet');
            tweetCollection.insert(tweet);
          }
        });

        res.on('close', function(err) {
          console.log(err);
        });
      });
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const path = require('path');

// uploadObject (task: Task, fileName: String) => Promise
function uploadObject (task, fileName) {
  const cwd = task.cwd;
  let stream = fs.createReadStream(path.join(cwd, fileName));
  task.emit('uploading', fileName);

  // lookup details in our local files Map
  let meta;
  try {
    meta = task.files.get(fileName);
  } catch (err) {
    const error = new Error('metadata for ' + fileName + ' unavailable');
    task.emit('error', error, fileName);
    return Promise.reject(error);
  }

  // make request to AWS S3
  return new Promise((resolve, reject) => {
    task.s3.upload({
      Body: stream,
      ContentType: meta.ContentType,
      Key: fileName
    }, (err, data) => {
      if (err) {
        reject(err);
        return;
      }
      resolve(data);
    });
  })
    .then(() => {
      task.emit('uploaded', fileName);
    })
    .catch((err) => {
      task.emit('error', err, fileName);
      throw err;
    });
}

module.exports = {
  uploadObject,

  // uploadObjects (task: Task, fileList: String[]) => Promise
  uploadObjects (task, fileList) {
    return Promise.all(fileList.map((fileName) => uploadObject(task, fileName)));
  }
};
<commit_msg>feature(upload): add acl<commit_after>'use strict';

const fs = require('fs');
const path = require('path');

// uploadObject (task: Task, fileName: String) => Promise
function uploadObject (task, fileName) {
  const cwd = task.cwd;
  let stream = fs.createReadStream(path.join(cwd, fileName));
  task.emit('uploading', fileName);

  // lookup details in our local files Map
  let meta;
  try {
    meta = task.files.get(fileName);
  } catch (err) {
    const error = new Error('metadata for ' + fileName + ' unavailable');
    task.emit('error', error, fileName);
    return Promise.reject(error);
  }

  // make request to AWS S3
  return new Promise((resolve, reject) => {
    task.s3.upload({
      Body: stream,
      ContentType: meta.ContentType,
      Key: fileName,
      ACL: task.ACL
    }, (err, data) => {
      if (err) {
        reject(err);
        return;
      }
      resolve(data);
    });
  })
    .then(() => {
      task.emit('uploaded', fileName);
    })
    .catch((err) => {
      task.emit('error', err, fileName);
      throw err;
    });
}

module.exports = {
  uploadObject,

  // uploadObjects (task: Task, fileList: String[]) => Promise
  uploadObjects (task, fileList) {
    return Promise.all(fileList.map((fileName) => uploadObject(task, fileName)));
  }
};
<|endoftext|>


<commit_before>'use babel';

import {spawn} from './process';

//   ____                      _ _             ____                 _ _
//  / ___|___  _ __ ___  _ __ (_) | ___ _ __  |  _ \ ___  ___ _   _| | |_ ___
// | |   / _ \| '_ ` _ \| '_ \| | |/ _ \ '__| | |_) / _ \/ __| | | | | __/ __|
// | |__| (_) | | | | | | |_) | | |  __/ |    |  _ <  __/\__ \ |_| | | |_\__ \
//  \____\___/|_| |_| |_| .__/|_|_|\___|_|    |_| \_\___||___/\__,_|_|\__|___/
//                      |_|

export function getBuildJSON(filePath) {
	return new Promise(resolve => {
		const command = atom.config.get('crystal.compilerExecPath');
		const args = getCommandArguments(filePath);
		createProperCrystalPath().then(path => {
			const env = Object.assign({}, process.env, {CRYSTAL_PATH: path});
			spawn(command, args, {env}).then(output => {
				resolve(output);
			});
		});
	});
}

//   ____                                          _   ____        _ _     _ _
//  / ___|___  _ __ ___  _ __ ___   __ _ _ __   __| | | __ ) _   _(_) | __| (_)_ __   __ _
// | |   / _ \| '_ ` _ \| '_ ` _ \ / _` | '_ \ / _` | |  _ \| | | | | |/ _` | | '_ \ / _` |
// | |__| (_) | | | | | | | | | | | (_| | | | | (_| | | |_) | |_| | | | (_| | | | | | (_| |
//  \____\___/|_| |_| |_|_| |_| |_|\__,_|_| |_|\__,_| |____/ \__,_|_|_|\__,_|_|_| |_|\__, |
//                                                                                |___/

export function getCommandArguments(filePath) {
	const args = [];
	args.push(atom.config.get('crystal.diagnosticsCommand'));
	if (!atom.config.get('crystal.artifactsDuringDiagnostics')) {
		args.push('--no-codegen');
	}
	if (!atom.config.get('crystal.compilerColorOutput')) {
		args.push('--no-color');
	}
	args.push('-f', 'json');
	args.push(filePath);
	return args;
}

//  ____       _   _       _   _                 _ _ _
// |  _ \ __ _| |_| |__   | | | | __ _ _ __   __| | (_)_ __   __ _
// | |_) / _` | __| '_ \  | |_| |/ _` | '_ \ / _` | | | '_ \ / _` |
// |  __/ (_| | |_| | | | |  _  | (_| | | | | (_| | | | | | | (_| |
// |_|   \__,_|\__|_| |_| |_| |_|\__,_|_| |_|\__,_|_|_|_| |_|\__, |
//                                                           |___/

export function createProperCrystalPath() {
	const projectPath = `${atom.project.getPaths()[0]}/libs`;
	return new Promise(resolve => {
		findDefaultCrystalPath().then(output => {
			resolve(`${output}:${projectPath}`);
		});
	});
}

export function findDefaultCrystalPath() {
	const regex = /CRYSTAL_PATH="(.*)"\n/;
	const spawn = require('child_process').spawn;

	let output = '';

	return new Promise(resolve => {
		const command = spawn('crystal', ['env']);
		command.stdout.on('data', data => {
			output = data.toString();
		});
		command.on('close', () => {
			const match = regex.exec(output);
			if (match.length === 2) {
				resolve(match[1]);
			}
		});
	});
}
<commit_msg>quick check for null<commit_after>'use babel';

import {spawn} from './process';

//   ____                      _ _             ____                 _ _
//  / ___|___  _ __ ___  _ __ (_) | ___ _ __  |  _ \ ___  ___ _   _| | |_ ___
// | |   / _ \| '_ ` _ \| '_ \| | |/ _ \ '__| | |_) / _ \/ __| | | | | __/ __|
// | |__| (_) | | | | | | |_) | | |  __/ |    |  _ <  __/\__ \ |_| | | |_\__ \
//  \____\___/|_| |_| |_| .__/|_|_|\___|_|    |_| \_\___||___/\__,_|_|\__|___/
//                      |_|

export function getBuildJSON(filePath) {
	return new Promise(resolve => {
		const command = atom.config.get('crystal.compilerExecPath');
		const args = getCommandArguments(filePath);
		createProperCrystalPath().then(path => {
			const env = Object.assign({}, process.env, {CRYSTAL_PATH: path});
			spawn(command, args, {env}).then(output => {
				resolve(output);
			});
		});
	});
}

//   ____                                          _   ____        _ _     _ _
//  / ___|___  _ __ ___  _ __ ___   __ _ _ __   __| | | __ ) _   _(_) | __| (_)_ __   __ _
// | |   / _ \| '_ ` _ \| '_ ` _ \ / _` | '_ \ / _` | |  _ \| | | | | |/ _` | | '_ \ / _` |
// | |__| (_) | | | | | | | | | | | (_| | | | | (_| | | |_) | |_| | | | (_| | | | | | (_| |
//  \____\___/|_| |_| |_|_| |_| |_|\__,_|_| |_|\__,_| |____/ \__,_|_|_|\__,_|_|_| |_|\__, |
//                                                                                |___/

export function getCommandArguments(filePath) {
	const args = [];
	args.push(atom.config.get('crystal.diagnosticsCommand'));
	if (!atom.config.get('crystal.artifactsDuringDiagnostics')) {
		args.push('--no-codegen');
	}
	if (!atom.config.get('crystal.compilerColorOutput')) {
		args.push('--no-color');
	}
	args.push('-f', 'json');
	args.push(filePath);
	return args;
}

//  ____       _   _       _   _                 _ _ _
// |  _ \ __ _| |_| |__   | | | | __ _ _ __   __| | (_)_ __   __ _
// | |_) / _` | __| '_ \  | |_| |/ _` | '_ \ / _` | | | '_ \ / _` |
// |  __/ (_| | |_| | | | |  _  | (_| | | | | (_| | | | | | | (_| |
// |_|   \__,_|\__|_| |_| |_| |_|\__,_|_| |_|\__,_|_|_|_| |_|\__, |
//                                                           |___/

export function createProperCrystalPath() {
	const projectPath = `${atom.project.getPaths()[0]}/libs`;
	return new Promise(resolve => {
		findDefaultCrystalPath().then(output => {
			resolve(`${output}:${projectPath}`);
		});
	});
}

export function findDefaultCrystalPath() {
	const regex = /CRYSTAL_PATH="(.*)"\n/;
	const spawn = require('child_process').spawn;

	let output = '';

	return new Promise(resolve => {
		const command = spawn('crystal', ['env']);
		command.stdout.on('data', data => {
			output = data.toString();
		});
		command.on('close', () => {
			const match = regex.exec(output);
			if (!match && match.length === 2) {
				resolve(match[1]);
			}
		});
	});
}
<|endoftext|>


<commit_before>'use strict';

var Promise = require('native-or-bluebird');
var util = require('util');
var async = require('async');

var EventEmitter = require('events').EventEmitter;

var Hookable = function Hookable() {
  EventEmitter.call(this);

  if(!this.hooks) {
    throw new Error('No hooks are defined in this object.');
  }

  this.hookCallbacks = {};

  this.hookMap = {};

  this.hooks.forEach(function(hook) {
    this.hookMap[hook] = 1;
  }, this);
};

util.inherits(Hookable, EventEmitter);

Hookable.prototype.callHook = function callHook(hook, args, cb) {
  if(!this.hookMap[hook]) {
    throw new Error('The hook `' + hook + '` is not defined.');
  }

  if(!(this.hookCallbacks[hook] && this.hookCallbacks[hook].length)) {
    return Promise.resolve(null).nodeify(cb);
  }

  var self = this;
  return new Promise(function(resolve, reject) {
    function iterator(fn, next) {
      fn.call(this, args, next);
    }

    async.each(
      self.hookCallbacks[hook],
      iterator.bind(self),
      function(err) {
        if(err) {
          reject(err);
        } else {
          resolve();
        }
      }
    )
  }).nodeify(cb);
};

Hookable.prototype.performHookCycle = function performHookCycle(hook, args, fn, done) {
  var self = this;

  async.series([
    function willHook(next) {
      return self.callHook('will' + hook, args, next);
    },
    fn,
    function didHook(next) {
      return self.callHook('did' + hook, args, next);
    },
  ], function(err, results) {
    done(err, results);
  });
};

Hookable.prototype.hook = function hookF(hook, fn) {
  if(!this.hookMap[hook]) {
    throw new Error('The hook `' + hook + '` is not defined.');
  }

  if(!this.hookCallbacks[hook]) {
    this.hookCallbacks[hook] = [];
  }

  this.hookCallbacks[hook].push(fn);
};

Hookable.prototype.unhook = function unhook(hook, fn) {
  if(!this.hookMap[hook]) {
    throw new Error('The hook `' + hook + '` is not defined.');
  }

  var index = this.hookCallbacks[hook]
                ? this.hookCallbacks[hook].indexOf(fn)
                : -1;

  if(index != -1) {
    throw new Error('The callback `' + callback + '` is not registered.');
  }

  this.hookCallbacks[hook].splice(index, 1);
};

module.exports = Hookable;
<commit_msg>code: promisify hookable.performHookCycle<commit_after>'use strict';

var Promise = require('native-or-bluebird');
var util = require('util');
var async = require('async');

var EventEmitter = require('events').EventEmitter;

var Hookable = function Hookable() {
  EventEmitter.call(this);

  if(!this.hooks) {
    throw new Error('No hooks are defined in this object.');
  }

  this.hookCallbacks = {};

  this.hookMap = {};

  this.hooks.forEach(function(hook) {
    this.hookMap[hook] = 1;
  }, this);
};

util.inherits(Hookable, EventEmitter);

Hookable.prototype.callHook = function callHook(hook, args, cb) {
  if(!this.hookMap[hook]) {
    throw new Error('The hook `' + hook + '` is not defined.');
  }

  if(!(this.hookCallbacks[hook] && this.hookCallbacks[hook].length)) {
    return Promise.resolve(null).nodeify(cb);
  }

  var self = this;
  return new Promise(function(resolve, reject) {
    function iterator(fn, next) {
      fn.call(this, args, next);
    }

    async.each(
      self.hookCallbacks[hook],
      iterator.bind(self),
      function(err) {
        if(err) {
          reject(err);
        } else {
          resolve();
        }
      }
    )
  }).nodeify(cb);
};

Hookable.prototype.performHookCycle = function performHookCycle(hook, args, fn, done) {
  var self = this;

  return new Promise(function(resolve, reject) {
    async.series([
      function willHook(next) {
        return self.callHook('will' + hook, args, next);
      },
      fn,
      function didHook(next) {
        return self.callHook('did' + hook, args, next);
      },
    ], function(err, results) {
      if(err) {
        reject(err);
      } else {
        resolve(results);
      }
    });
  }).nodeify(done);
};

Hookable.prototype.hook = function hookF(hook, fn) {
  if(!this.hookMap[hook]) {
    throw new Error('The hook `' + hook + '` is not defined.');
  }

  if(!this.hookCallbacks[hook]) {
    this.hookCallbacks[hook] = [];
  }

  this.hookCallbacks[hook].push(fn);
};

Hookable.prototype.unhook = function unhook(hook, fn) {
  if(!this.hookMap[hook]) {
    throw new Error('The hook `' + hook + '` is not defined.');
  }

  var index = this.hookCallbacks[hook]
                ? this.hookCallbacks[hook].indexOf(fn)
                : -1;

  if(index != -1) {
    throw new Error('The callback `' + callback + '` is not registered.');
  }

  this.hookCallbacks[hook].splice(index, 1);
};

module.exports = Hookable;
<|endoftext|>


<commit_before>var http = require('http');
var https = require('https');
var fs = require('fs');
var extend = require('extend');
var fileMgr = require('./file-mgr');
var logger = require('./logger');
var parseUrl = require('./parse-url');
var zlib = require('./zlib');

var cache = {};
var listeners = [];
var newUrls;
var TIMEOUT = 16000;
var MAX_RULES_LEN = 1024 * 72;
var MAX_FILE_LEN = 1024 * 256;
var MAX_INTERVAL = 1000 * 30;
var MIN_INTERVAL = 1000 * 10;
var EXCEED = 'EXCEED';
var OPTIONS = { encoding: 'utf8' };
var queue = [];
var queueTimer;
var FILE_RE = /^(?:[a-z]:[\\/]|[~～]?\/)/i;
var GZIP_RE = /gzip/i;
var pendingList = process.whistleStarted ? null : [];
var pluginMgr;

process.once('whistleStarted', function () {
  if (pendingList) {
    pendingList.forEach(function (item) {
      add(item[0], item[1], item[2]);
    });
    pendingList = null;
  }
});

function getInterval(time, isLocal) {
  var len = Object.keys(cache).length || 1;
  var interval = isLocal
    ? 5000
    : Math.max(MIN_INTERVAL, Math.ceil(MAX_INTERVAL / len));
  var minTime = interval - (time > 0 ? time : 0);
  return Math.max(minTime, 1000);
}

function triggerChange(data, body) {
  if (data) {
    body = (body && body.trim()) || '';
    if (data.body === body) {
      return;
    }
    data.body = body;
  }
  if (newUrls) {
    return;
  }
  newUrls = {};
  listeners.forEach(function (l) {
    l();
  });
  Object.keys(newUrls).forEach(function (url) {
    newUrls[url] = cache[url];
  });
  cache = newUrls;
  newUrls = null;
}

function parseOptions(options) {
  if (typeof options === 'string') {
    options = parseUrl(options);
  } else {
    var fullUrl = options.url || options.uri;
    if (fullUrl && typeof fullUrl === 'string') {
      options = extend(options, parseUrl(fullUrl));
    }
  }
  var maxLength = options.maxLength;
  if (!(maxLength > 0)) {
    options.maxLength = 0;
  }
  options.agent = false;
  if (options.rejectUnauthorized !== true) {
    options.rejectUnauthorized = false;
  }
  if (options.headers && options.headers.trailer) {
    delete options.headers.trailer;
  }
  return options;
}

function toString(obj) {
  if (obj == null) {
    return;
  }
  if (typeof obj === 'object') {
    return JSON.stringify(obj);
  }
  if (Buffer.isBuffer(obj)) {
    return obj;
  }
  return obj + '';
}

var NOT_PLUGIN_ERR = new Error('Error: not found');
var NOT_UI_SERVER_ERR = new Error('Error: not implemented uiServer');
NOT_PLUGIN_ERR.code = 404;
NOT_UI_SERVER_ERR.code = 501;

function loadPlugin(options, callback) {
  var name = options.pluginName;
  if (!name) {
    return callback();
  }
  pluginMgr.loadPluginByName(name, function (err, ports) {
    if (err || !ports || !ports.uiPort) {
      return callback(err || (ports ? NOT_UI_SERVER_ERR : NOT_PLUGIN_ERR));
    }
    options.url =
      'http://127.0.0.1:' + ports.uiPort + options.url.substring(name.length);
    callback();
  });
}

function request(options, callback) {
  loadPlugin(options, function (err) {
    if (err) {
      return callback(err, '', '');
    }
    options = parseOptions(options);
    var isHttps = options.protocol === 'https:';
    var httpModule = isHttps ? https : http;
    var done, timer, res;
    var body = '';
    var callbackHandler = function (err) {
      clearTimeout(timer);
      err && client && client.abort();
      if (!done) {
        done = true;
        var handleCallback = function(e, data) {
          data = e ? '' : (options.needRawData ? data : data + '');
          callback(e, data, res || '');
        };
        if (res && body && GZIP_RE.test(res.headers['content-encoding'])) {
          zlib.gunzip(body, handleCallback);
        } else {
          handleCallback(err, body);
        }
      }
    };
    var addTimeout = function () {
      clearTimeout(timer);
      timer = setTimeout(function () {
        callbackHandler(new Error('Timeout'));
      }, TIMEOUT);
    };
    addTimeout();
    var maxLength = options.maxLength;
    try {
      var client = httpModule.request(options, function (r) {
        res = r;
        res.on('error', callbackHandler);
        res.on('data', function (data) {
          body = body ? Buffer.concat([body, data]) : data;
          addTimeout();
          if (maxLength && body.length > maxLength) {
            var err;
            if (!options.ignoreExceedError) {
              err = new Error('The response body exceeded length limit');
              err.code = EXCEED;
            }
            callbackHandler(err);
          }
        });
        res.on('end', callbackHandler);
      });
      client.on('error', callbackHandler);
      client.end(toString(options.body));
    } catch (e) {
      callbackHandler(e);
    }
  });
}

exports.request = request;

function readFile(url, callback) {
  var data;
  var now = Date.now();
  var execCallback = function () {
    callback(url, Date.now() - now);
  };
  var filePath = fileMgr.convertSlash(url);
  fs.stat(filePath, function (err, stat) {
    data = cache[url];
    if (!data) {
      return execCallback();
    }
    if (err) {
      if (err.code === 'ENOENT') {
        err = null;
      } else {
        logger.error(url, err.message);
      }
      triggerChange(data);
      data.mtime = null;
      return execCallback();
    }
    if (!stat.isFile()) {
      triggerChange(data);
      data.mtime = null;
      return execCallback();
    }
    var time = stat.mtime.getTime();
    if (time === data.mtime) {
      return execCallback();
    }

    var stream = fs.createReadStream(filePath, OPTIONS);
    var done;
    var body = '';
    var listener = function (err) {
      if (done) {
        return;
      }
      execCallback();
      if (err && err.code !== 'ENOENT') {
        return;
      }
      done = true;
      data.mtime = time;
      stream.close();
      triggerChange(data, body);
    };
    stream.on('data', function (text) {
      if (done) {
        return;
      }
      body += text;
      if (body.length > MAX_FILE_LEN) {
        listener();
      }
    });
    stream.on('error', listener);
    stream.on('end', listener);
  });
}

function addQueue(url, consumeTime) {
  if (cache[url] && queue.indexOf(url) === -1) {
    queue.push(url);
  }
  var data;
  while (!queueTimer && !data) {
    url = queue.shift();
    if (!url) {
      return;
    }
    data = cache[url];
    if (data) {
      queueTimer = setTimeout(function () {
        queueTimer = null;
        updateBody(url, addQueue);
      }, getInterval(consumeTime, data.isLocalUrl || data.isLocalPath));
      return;
    }
  }
}

function updateBody(url, callback, init) {
  var data = cache[url];
  if (!data) {
    return callback && callback();
  }
  if (data.isLocalPath) {
    return readFile(url, addQueue);
  }
  var now = Date.now();
  var options = {
    url: url,
    pluginName: data.pluginName,
    maxLength: MAX_RULES_LEN,
    ignoreExceedError: true
  };
  if (data.headers) {
    options.headers = data.headers;
  }
  request(options, function (err, body, res) {
    data = cache[url];
    callback && callback(url, Date.now() - now);
    if (!data) {
      return;
    }
    var code = res.statusCode;
    var notFound = err
      ? err.code === 'ENOTFOUND' || err.code === 'ECONNREFUSED'
      : code != 200 && code != 204;
    err && logger.error('[Load Rules]', url, err.message || err);
    if (notFound) {
      data._retry = data._retry || 0;
      if (data._retry > 2) {
        !err && logger.warn('[Load Rules]', url, 'status', code);
        data._retry = -6;
        err = body = '';
        notFound = false;
      }
      ++data._retry;
    } else {
      data._retry = 0;
    }
    if (notFound || err) {
      if (init) {
        updateBody(url);
        return;
      }
    }
    addQueue(url);
    if (notFound || err) {
      return;
    }
    triggerChange(data, body);
  });
  return true;
}

exports.addChangeListener = function (l) {
  listeners.push(l);
};

function add(url, headers, pluginName) {
  var data = cache[url];
  if (!data) {
    cache[url] = data = {
      body: '',
      pluginName: pluginName,
      isLocalUrl: pluginName || url.indexOf('http://127.0.0.1:') === 0,
      isLocalPath: FILE_RE.test(url),
      headers: headers
    };
    updateBody(url, null, true);
  }
  if (newUrls) {
    newUrls[url] = 1;
  }
  return data.body;
}

exports.add = function (url, headers, pluginName) {
  if (pendingList && headers && headers['x-whistle-internal-id']) {
    pendingList.push([url, headers, pluginName]);
    return '';
  }
  return add(url, headers, pluginName);
};

exports.forceUpdate = function (root) {
  Object.keys(cache).forEach(function (url) {
    if (url.indexOf(root) === 0) {
      updateBody(url);
    }
  });
};

exports.clean = function () {
  if (!newUrls && Object.keys(cache).length) {
    triggerChange();
  }
};

exports.setPluginMgr = function (mgr) {
  pluginMgr = mgr;
};
<commit_msg>refactor: refine http-mgr<commit_after>var http = require('http');
var https = require('https');
var fs = require('fs');
var extend = require('extend');
var fileMgr = require('./file-mgr');
var logger = require('./logger');
var parseUrl = require('./parse-url');
var zlib = require('./zlib');

var cache = {};
var listeners = [];
var newUrls;
var TIMEOUT = 16000;
var MAX_RULES_LEN = 1024 * 72;
var MAX_FILE_LEN = 1024 * 256;
var MAX_INTERVAL = 1000 * 30;
var MIN_INTERVAL = 1000 * 10;
var EXCEED = 'EXCEED';
var OPTIONS = { encoding: 'utf8' };
var queue = [];
var queueTimer;
var FILE_RE = /^(?:[a-z]:[\\/]|[~～]?\/)/i;
var GZIP_RE = /gzip/i;
var pendingList = process.whistleStarted ? null : [];
var pluginMgr;

process.once('whistleStarted', function () {
  if (pendingList) {
    pendingList.forEach(function (item) {
      add(item[0], item[1], item[2]);
    });
    pendingList = null;
  }
});

function getInterval(time, isLocal) {
  var len = Object.keys(cache).length || 1;
  var interval = isLocal
    ? 5000
    : Math.max(MIN_INTERVAL, Math.ceil(MAX_INTERVAL / len));
  var minTime = interval - (time > 0 ? time : 0);
  return Math.max(minTime, 1000);
}

function triggerChange(data, body) {
  if (data) {
    body = (body && body.trim()) || '';
    if (data.body === body) {
      return;
    }
    data.body = body;
  }
  if (newUrls) {
    return;
  }
  newUrls = {};
  listeners.forEach(function (l) {
    l();
  });
  Object.keys(newUrls).forEach(function (url) {
    newUrls[url] = cache[url];
  });
  cache = newUrls;
  newUrls = null;
}

function parseOptions(options) {
  if (typeof options === 'string') {
    options = parseUrl(options);
  } else {
    var fullUrl = options.url || options.uri;
    if (fullUrl && typeof fullUrl === 'string') {
      options = extend(options, parseUrl(fullUrl));
    }
  }
  var maxLength = options.maxLength;
  if (!(maxLength > 0)) {
    options.maxLength = 0;
  }
  options.agent = false;
  if (options.rejectUnauthorized !== true) {
    options.rejectUnauthorized = false;
  }
  if (options.headers && options.headers.trailer) {
    delete options.headers.trailer;
  }
  return options;
}

function toString(obj) {
  if (obj == null) {
    return;
  }
  if (typeof obj === 'object') {
    return JSON.stringify(obj);
  }
  if (Buffer.isBuffer(obj)) {
    return obj;
  }
  return obj + '';
}

var NOT_PLUGIN_ERR = new Error('Error: not found');
var NOT_UI_SERVER_ERR = new Error('Error: not implemented uiServer');
NOT_PLUGIN_ERR.code = 404;
NOT_UI_SERVER_ERR.code = 501;

function loadPlugin(options, callback) {
  var name = options.pluginName;
  if (!name) {
    return callback();
  }
  pluginMgr.loadPluginByName(name, function (err, ports) {
    if (err || !ports || !ports.uiPort) {
      return callback(err || (ports ? NOT_UI_SERVER_ERR : NOT_PLUGIN_ERR));
    }
    options.url =
      'http://127.0.0.1:' + ports.uiPort + options.url.substring(name.length);
    callback();
  });
}

function sendReq(options, callback) {
  var httpModule = options.protocol === 'https:' ? https : http;
  var timer, client;
  var handleCallback = function (err, res) {
    clearTimeout(timer);
    if (httpModule) {
      httpModule = null;
      callback(err, res);
    }
    err && client && client.destroy();
  };
  timer = setTimeout(function () {
    handleCallback(new Error('Timeout'));
  }, TIMEOUT);
  try {
    client = httpModule.request(options, function (res) {
      res.on('error', handleCallback);
      handleCallback(null, res);
    });
    client.on('error', handleCallback);
    client.end(toString(options.body));
    return client;
  } catch (e) {
    handleCallback(e);
  }
}

function gunzip(err, res, body, callback) {
  if (!err && body && res && GZIP_RE.test(res.headers['content-encoding'])) {
    zlib.gunzip(body, callback);
  } else {
    callback(err, body);
  }
}

function request(options, callback) {
  loadPlugin(options, function (err) {
    if (err) {
      return callback(err, '', '');
    }
    options = parseOptions(options);
    var done, client, timer;
    var handleCallback = function (err, res, body) {
      if (!done) {
        done = true;
        gunzip(err, res, body, function(e, data) {
          data = e ? '' : (options.needRawData ? data : data + '');
          callback(e, data, res || '');
        });
      }
      clearTimeout(timer);
      err && client && client.destroy();
    };
    var addTimeout = function () {
      clearTimeout(timer);
      timer = setTimeout(function () {
        handleCallback(new Error('Timeout'));
      }, TIMEOUT);
    };
    var maxLength = options.maxLength;
    var handleResponse = function(res) {
      addTimeout();
      var body = '';
      res.on('data', function (data) {
        body = body ? Buffer.concat([body, data]) : data;
        addTimeout();
        if (maxLength && body.length > maxLength) {
          var err;
          if (!options.ignoreExceedError) {
            err = new Error('The response body exceeded length limit');
            err.code = EXCEED;
          }
          handleCallback(err, res, body);
        }
      });
      res.on('end', function() {
        handleCallback(null, res, body);
      });
    };
    client = sendReq(options, function (err, res) {
      if (err) {
        handleCallback(err);
      } else {
        handleResponse(res);
      }
    });
  });
}

exports.request = request;

function readFile(url, callback) {
  var data;
  var now = Date.now();
  var execCallback = function () {
    callback(url, Date.now() - now);
  };
  var filePath = fileMgr.convertSlash(url);
  fs.stat(filePath, function (err, stat) {
    data = cache[url];
    if (!data) {
      return execCallback();
    }
    if (err) {
      if (err.code === 'ENOENT') {
        err = null;
      } else {
        logger.error(url, err.message);
      }
      triggerChange(data);
      data.mtime = null;
      return execCallback();
    }
    if (!stat.isFile()) {
      triggerChange(data);
      data.mtime = null;
      return execCallback();
    }
    var time = stat.mtime.getTime();
    if (time === data.mtime) {
      return execCallback();
    }

    var stream = fs.createReadStream(filePath, OPTIONS);
    var done;
    var body = '';
    var listener = function (err) {
      if (done) {
        return;
      }
      execCallback();
      if (err && err.code !== 'ENOENT') {
        return;
      }
      done = true;
      data.mtime = time;
      stream.close();
      triggerChange(data, body);
    };
    stream.on('data', function (text) {
      if (done) {
        return;
      }
      body += text;
      if (body.length > MAX_FILE_LEN) {
        listener();
      }
    });
    stream.on('error', listener);
    stream.on('end', listener);
  });
}

function addQueue(url, consumeTime) {
  if (cache[url] && queue.indexOf(url) === -1) {
    queue.push(url);
  }
  var data;
  while (!queueTimer && !data) {
    url = queue.shift();
    if (!url) {
      return;
    }
    data = cache[url];
    if (data) {
      queueTimer = setTimeout(function () {
        queueTimer = null;
        updateBody(url, addQueue);
      }, getInterval(consumeTime, data.isLocalUrl || data.isLocalPath));
      return;
    }
  }
}

function updateBody(url, callback, init) {
  var data = cache[url];
  if (!data) {
    return callback && callback();
  }
  if (data.isLocalPath) {
    return readFile(url, addQueue);
  }
  var now = Date.now();
  var options = {
    url: url,
    pluginName: data.pluginName,
    maxLength: MAX_RULES_LEN,
    ignoreExceedError: true
  };
  if (data.headers) {
    options.headers = data.headers;
  }
  request(options, function (err, body, res) {
    data = cache[url];
    callback && callback(url, Date.now() - now);
    if (!data) {
      return;
    }
    var code = res.statusCode;
    var notFound = err
      ? err.code === 'ENOTFOUND' || err.code === 'ECONNREFUSED'
      : code != 200 && code != 204;
    err && logger.error('[Load Rules]', url, err.message || err);
    if (notFound) {
      data._retry = data._retry || 0;
      if (data._retry > 2) {
        !err && logger.warn('[Load Rules]', url, 'status', code);
        data._retry = -6;
        err = body = '';
        notFound = false;
      }
      ++data._retry;
    } else {
      data._retry = 0;
    }
    if (notFound || err) {
      if (init) {
        updateBody(url);
        return;
      }
    }
    addQueue(url);
    if (notFound || err) {
      return;
    }
    triggerChange(data, body);
  });
  return true;
}

exports.addChangeListener = function (l) {
  listeners.push(l);
};

function add(url, headers, pluginName) {
  var data = cache[url];
  if (!data) {
    cache[url] = data = {
      body: '',
      pluginName: pluginName,
      isLocalUrl: pluginName || url.indexOf('http://127.0.0.1:') === 0,
      isLocalPath: FILE_RE.test(url),
      headers: headers
    };
    updateBody(url, null, true);
  }
  if (newUrls) {
    newUrls[url] = 1;
  }
  return data.body;
}

exports.add = function (url, headers, pluginName) {
  if (pendingList && headers && headers['x-whistle-internal-id']) {
    pendingList.push([url, headers, pluginName]);
    return '';
  }
  return add(url, headers, pluginName);
};

exports.forceUpdate = function (root) {
  Object.keys(cache).forEach(function (url) {
    if (url.indexOf(root) === 0) {
      updateBody(url);
    }
  });
};

exports.clean = function () {
  if (!newUrls && Object.keys(cache).length) {
    triggerChange();
  }
};

exports.setPluginMgr = function (mgr) {
  pluginMgr = mgr;
};
<|endoftext|>


<commit_before>/* @flow weak */
require(`node-cjsx`).transform()
import detect from 'detect-port'
import Hapi from 'hapi'
import Boom from 'boom'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import webpack from 'webpack'
import Negotiator from 'negotiator'
import parsePath from 'parse-filepath'
import _ from 'lodash'
import webpackRequire from 'webpack-require'
import opn from 'opn'
import fs from 'fs'
import glob from 'glob'
import rl from 'readline'
import GraphQL from 'hapi-graphql'
import WebpackDevServer from 'webpack-dev-server'
import { pagesDB } from './globals'

import bootstrap from '../bootstrap'

const rlInterface = rl.createInterface({
  input: process.stdin,
  output: process.stdout,
})

import webpackConfig from './webpack.config'
const debug = require(`debug`)(`gatsby:application`)

function startServer (program) {
  const directory = program.directory

  // Load pages for the site.
  return bootstrap(program, (err, schema) => {
    // Generate random port for webpack to listen on.
    // Perhaps should check if port is open.
    const webpackPort = Math.round(Math.random() * 1000 + 1000)
    const compilerConfig = webpackConfig(program, directory, `develop`, webpackPort)

    const compiler = webpack(compilerConfig.resolve())

    let HTMLPath = `${directory}/html`
    // Check if we can't find an html component in root of site.
    if (glob.sync(`${HTMLPath}.*`).length === 0) {
      HTMLPath = `../isomorphic/html`
    }

    // We use the program port not the webpack-dev-server port as if you import
    // files in your html.js they won't be available through the webpack-dev-server.
    // By using the program port, requesting these imported files might accidentally work
    // as the imported files will be available in /public. TODO test how expensive
    // it'd be to do an actual static compile of the html.js on startup to avoid
    // this discprenecy between dev and prod.
    const htmlCompilerConfig = webpackConfig(program, directory, `develop-html`, program.port)

    webpackRequire(htmlCompilerConfig.resolve(), require.resolve(HTMLPath), (error, factory) => {
      if (error) {
        console.log(`Failed to require ${directory}/html.js`)
        error.forEach((e) => {
          console.log(e)
        })
        process.exit()
      }
      const HTML = factory()
      debug(`Configuring develop server`)

      const webpackDevServer = new WebpackDevServer(compiler, {
        hot: true,
        quiet: false,
        noInfo: true,
        host: program.host,
        headers: {
          'Access-Control-Allow-Origin': '*',
        },
        stats: {
          colors: true,
        },
      })

      // Start webpack-dev-server
      webpackDevServer.listen(webpackPort, program.host)

      const server = new Hapi.Server()

      server.connection({
        host: program.host,
        port: program.port,
      })

      // As our two processes (Webpack-Dev-Server + this Hapi.js server) are
      // running on different ports, we proxy requests for the bundle.js to
      // Webpack.
      server.route({
        method: 'GET',
        path: '/commons.js',
        handler: {
          proxy: {
            uri: `http://0.0.0.0:${webpackPort}/commons.js`,
            passThrough: true,
            xforward: true,
          },
        },
      })

      server.route({
        method: `GET`,
        path: `/html/{path*}`,
        handler: (request, reply) => {
          if (request.path === `favicon.ico`) {
            return reply(Boom.notFound())
          }

          try {
            const htmlElement = React.createElement(
              HTML, {
                body: ``,
                scripts: [`commons`],
                headComponents: [],
              }
            )
            let html = ReactDOMServer.renderToStaticMarkup(htmlElement)
            html = `<!DOCTYPE html>\n${html}`
            return reply(html)
          } catch (e) {
            console.log(e.stack)
            throw e
          }
        },
      })

      server.route({
        method: `GET`,
        path: `/{path*}`,
        handler: {
          directory: {
            path: `${program.directory}/public`,
            listing: false,
            index: false,
          },
        },
      })

      server.ext(`onRequest`, (request, reply) => {
        if (request.path === `/graphql`) {
          return reply.continue()
        }

        const negotiator = new Negotiator(request.raw.req)

        // Try to map the url path to match an actual path of a file on disk.
        //const parsed = parsePath(request.path)
        //const page = pagesDB().get(parsed.dirname)

        //let absolutePath = `${program.directory}/pages`
        //let path
        //if (page) {
          //path = `/${parsePath(page.component).dirname}/${parsed.basename}`
          //absolutePath += `/${parsePath(page.component).dirname}/${parsed.basename}`
        //} else {
          //path = request.path
          //absolutePath += request.path
        //}
        //let isFile = false
        //try {
          //isFile = fs.lstatSync(absolutePath).isFile()
        //} catch (e) {
          //// Ignore.
        //}

        // If the path matches a file, return that.
        //if (isFile) {
          //request.setUrl(path)
          //reply.continue()
        // Let people load the bundle.js directly.
        //} else if (request.path === `/bundle.js`) {
        if (request.path === `/bundle.js`) {
          reply.continue()
        } else if (negotiator.mediaType() === `text/html`) {
          request.setUrl(`/html${request.path}`)
          reply.continue()
        } else {
          reply.continue()
        }
      })

      return server.register([
        // Add GraphQL support
        {
          register: GraphQL,
          options: {
            query: {
              graphiql: true,
              pretty: true,
              schema,
            },
            route: {
              path: `/graphql`,
            },
          },
        },
      ], (er) => {
        if (er) {
          console.log(er)
          process.exit()
        }

        server.start((e) => {
          if (e) {
            if (e.code === `EADDRINUSE`) {
              // eslint-disable-next-line max-len
              console.log(`Unable to start Gatsby on port ${program.port} as there's already a process listing on that port.`)
            } else {
              console.log(e)
            }

            process.exit()
          } else {
            if (program.open) {
              opn(server.info.uri)
            }
            console.log(`Listening at:`, server.info.uri)
          }
        })
      })
    })
  })
}

module.exports = (program) => {
  const port = typeof program.port === 'string'
    ? parseInt(program.port, 10)
    : program.port

  detect(port, (err, _port) => {
    if (err) {
      console.error(err)
      process.exit()
    }

    if (port !== _port) {
      // eslint-disable-next-line max-len
      const question = `Something is already running at port ${port} \nWould you like to run the app at another port instead? [Y/n] `

      return rlInterface.question(question, (answer) => {
        if (answer.length === 0 || answer.match(/^yes|y$/i)) {
          program.port = _port // eslint-disable-line no-param-reassign
        }

        return startServer(program)
      })
    }

    return startServer(program)
  })
}
<commit_msg>Fix development server<commit_after>/* @flow weak */
require(`node-cjsx`).transform()
import detect from 'detect-port'
import Hapi from 'hapi'
import Boom from 'boom'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import webpack from 'webpack'
import Negotiator from 'negotiator'
import parsePath from 'parse-filepath'
import _ from 'lodash'
import webpackRequire from 'webpack-require'
import opn from 'opn'
import fs from 'fs'
import glob from 'glob'
import rl from 'readline'
import GraphQL from 'hapi-graphql'
import WebpackDevServer from 'webpack-dev-server'
import { pagesDB } from './globals'

import bootstrap from '../bootstrap'

const rlInterface = rl.createInterface({
  input: process.stdin,
  output: process.stdout,
})

import webpackConfig from './webpack.config'
const debug = require(`debug`)(`gatsby:application`)

function startServer (program) {
  const directory = program.directory

  // Load pages for the site.
  return bootstrap(program, (err, schema) => {
    // Generate random port for webpack to listen on.
    // Perhaps should check if port is open.
    const webpackPort = Math.round(Math.random() * 1000 + 1000)
    const compilerConfig = webpackConfig(program, directory, `develop`, webpackPort)

    const compiler = webpack(compilerConfig.resolve())

    let HTMLPath = `${directory}/html`
    // Check if we can't find an html component in root of site.
    if (glob.sync(`${HTMLPath}.*`).length === 0) {
      HTMLPath = `../isomorphic/html`
    }

    // We use the program port not the webpack-dev-server port as if you import
    // files in your html.js they won't be available through the webpack-dev-server.
    // By using the program port, requesting these imported files might accidentally work
    // as the imported files will be available in /public. TODO test how expensive
    // it'd be to do an actual static compile of the html.js on startup to avoid
    // this discprenecy between dev and prod.
    const htmlCompilerConfig = webpackConfig(program, directory, `develop-html`, program.port)

    webpackRequire(htmlCompilerConfig.resolve(), require.resolve(HTMLPath), (error, factory) => {
      if (error) {
        console.log(`Failed to require ${directory}/html.js`)
        error.forEach((e) => {
          console.log(e)
        })
        process.exit()
      }
      const HTML = factory()
      debug(`Configuring develop server`)

      const webpackDevServer = new WebpackDevServer(compiler, {
        hot: true,
        quiet: false,
        noInfo: true,
        host: program.host,
        headers: {
          'Access-Control-Allow-Origin': '*',
        },
        stats: {
          colors: true,
        },
      })

      // Start webpack-dev-server
      webpackDevServer.listen(webpackPort, program.host)

      const server = new Hapi.Server()

      server.connection({
        host: program.host,
        port: program.port,
      })

      // As our two processes (Webpack-Dev-Server + this Hapi.js server) are
      // running on different ports, we proxy requests for the bundle.js to
      // Webpack.
      server.route({
        method: 'GET',
        path: '/commons.js',
        handler: {
          proxy: {
            uri: `http://0.0.0.0:${webpackPort}/commons.js`,
            passThrough: true,
            xforward: true,
          },
        },
      })

      server.route({
        method: `GET`,
        path: `/html/{path*}`,
        handler: (request, reply) => {
          if (request.path === `favicon.ico`) {
            return reply(Boom.notFound())
          }

          try {
            const htmlElement = React.createElement(
              HTML, {
                body: ``,
                headComponents: [],
                postBodyComponents: [
                  <script src="/commons.js" />
                ]
              }
            )
            let html = ReactDOMServer.renderToStaticMarkup(htmlElement)
            html = `<!DOCTYPE html>\n${html}`
            return reply(html)
          } catch (e) {
            console.log(e.stack)
            throw e
          }
        },
      })

      server.route({
        method: `GET`,
        path: `/{path*}`,
        handler: {
          directory: {
            path: `${program.directory}/public`,
            listing: false,
            index: false,
          },
        },
      })

      server.ext(`onRequest`, (request, reply) => {
        if (request.path === `/graphql`) {
          return reply.continue()
        }

        const negotiator = new Negotiator(request.raw.req)

        // Try to map the url path to match an actual path of a file on disk.
        //const parsed = parsePath(request.path)
        //const page = pagesDB().get(parsed.dirname)

        //let absolutePath = `${program.directory}/pages`
        //let path
        //if (page) {
          //path = `/${parsePath(page.component).dirname}/${parsed.basename}`
          //absolutePath += `/${parsePath(page.component).dirname}/${parsed.basename}`
        //} else {
          //path = request.path
          //absolutePath += request.path
        //}
        //let isFile = false
        //try {
          //isFile = fs.lstatSync(absolutePath).isFile()
        //} catch (e) {
          //// Ignore.
        //}

        // If the path matches a file, return that.
        //if (isFile) {
          //request.setUrl(path)
          //reply.continue()
        // Let people load the bundle.js directly.
        //} else if (request.path === `/bundle.js`) {
        if (request.path === `/bundle.js`) {
          reply.continue()
        } else if (negotiator.mediaType() === `text/html`) {
          request.setUrl(`/html${request.path}`)
          reply.continue()
        } else {
          reply.continue()
        }
      })

      return server.register([
        // Add GraphQL support
        {
          register: GraphQL,
          options: {
            query: {
              graphiql: true,
              pretty: true,
              schema,
            },
            route: {
              path: `/graphql`,
            },
          },
        },
      ], (er) => {
        if (er) {
          console.log(er)
          process.exit()
        }

        server.start((e) => {
          if (e) {
            if (e.code === `EADDRINUSE`) {
              // eslint-disable-next-line max-len
              console.log(`Unable to start Gatsby on port ${program.port} as there's already a process listing on that port.`)
            } else {
              console.log(e)
            }

            process.exit()
          } else {
            if (program.open) {
              opn(server.info.uri)
            }
            console.log(`Listening at:`, server.info.uri)
          }
        })
      })
    })
  })
}

module.exports = (program) => {
  const port = typeof program.port === 'string'
    ? parseInt(program.port, 10)
    : program.port

  detect(port, (err, _port) => {
    if (err) {
      console.error(err)
      process.exit()
    }

    if (port !== _port) {
      // eslint-disable-next-line max-len
      const question = `Something is already running at port ${port} \nWould you like to run the app at another port instead? [Y/n] `

      return rlInterface.question(question, (answer) => {
        if (answer.length === 0 || answer.match(/^yes|y$/i)) {
          program.port = _port // eslint-disable-line no-param-reassign
        }

        return startServer(program)
      })
    }

    return startServer(program)
  })
}
<|endoftext|>


<commit_before>'use strict'

// packages
const moment = require('moment')
const Table = require('cli-table2')

// input moment which also contains days, output formated sum
const formatSum = (workHours) => {
  const hours = workHours.days() * 24 + workHours.hours()
  return toString(hours, workHours.minutes())
}

// input moment, output formated work hours
const formatWorkHours = (workHours) => (
  // to add negative sign
  toString(workHours.hours(), workHours.minutes())
)

// input hours and minutes, output string
const toString = (hours, minutes) => (
  `${hours} Hours and ${minutes} Minutes`
)

// input 'HH:mm', output moment object
const composeDateObject = (timeString) => {
  if (!timeString || timeString.length < 5) {
    return
  }
  const hour = timeString.split(':')[0]
  const minutes = timeString.split(':')[1]
  return moment({ hour, minutes })
}

// input start, end, pause
// output duration
const calculateWorkHours = (start, end, pause) => {
  // to assign hours to moment objects, we need the diff so current moment is fine
  const startDate = composeDateObject(start)
  const endDate = composeDateObject(end) || moment()
  const duration = moment.duration(endDate.diff(startDate.add({minutes: pause})))
  return duration
}

// to tell users when they can go home!
const shouldWorkUntil = (start, logger, CONFIG) => {
  const goHomeTime = composeDateObject(start)
    .add({hour: CONFIG.HOURS_IN_A_WORK_DAY})
    .add({minutes: CONFIG.BREAK_DEFAULT})
    .format('HH:mm')

  logger.info(
    '\n Working until ',
    goHomeTime,
    `will make it a full (${CONFIG.HOURS_IN_A_WORK_DAY}) day`
  )
}

// input obj. record
// output console.loggable table
const printSingleDayReport = (record) => {
// instantiate
  var table = new Table()

  table.push(
    { 'Today you worked:': record.dayReport },
    { 'Start:': record.start },
    { 'End:': record.end || 'Not set yet' },
    { 'Break duration:': record.breakDuration + ' minutes' },
    { 'Date:': record.date }
  )
  record.notes.forEach((note) => {
    if (!note) {
      return
    }
    table.push({'Note': note.createdat + ' ' + note.note})
  })
  return table.toString()
}

// full report of all days
const printAllDaysReport = (records, CONFIG) => {
  CONFIG = CONFIG || {}
  const pattern = CONFIG.DATE_FORMAT || 'YYYY-MM-DD'
  const table = buildTable(records, pattern)

  console.log('\n Report of all the days \n')
  console.log(table.toString())
}

// build a beautiful table
const buildTable = (records, pattern) => {
  let dayCounter = 0
  let sum
  let week = -1

  const table = new Table({
    head: [{colSpan: 2, content: 'Date'}, 'Work Hours'],
    style: {head: [], border: []},
    colWidths: [10, 15, 28]
  })

  records.forEach((record) => {
    const date = moment(record.date)
    const weekNumber = date.week()
    const workHours = record.workHours

    if (weekNumber > week) {
      // add sum if the week actually changes, but not at the beginning
      if (dayCounter >= 1) {
        table.push(sumRow(sum, week))
      }
      sum = workHours
      dayCounter = 0
      week = weekNumber
    } else {
      sum.add(workHours)
      dayCounter += 1
    }

    table.push([{colSpan: 2, content: date.format(pattern)}, formatWorkHours(workHours)])
    addNotes(table, record)
  })
  // make sure there is a sum if it was not added before and we have more than one row
  if (dayCounter >= 1) {
    table.push(sumRow(sum, week))
  }
  return table
}

const addNotes = (table, record) => {
  record.notes && record.notes.forEach((note) => {
    table.push([{colSpan: 2, content: '  ' + note.createdat}, note.note])
  })
}

const sumRow = (sum, week) => (
  [{colSpan: 2, content: `Sum in week ${week}`}, formatSum(sum)]
)

module.exports = {
  composeDateObject,
  printSingleDayReport,
  printAllDaysReport,
  shouldWorkUntil,
  calculateWorkHours,
  formatWorkHours
}
<commit_msg>sum even after only one week<commit_after>'use strict'

// packages
const moment = require('moment')
const Table = require('cli-table2')

// input moment which also contains days, output formated sum
const formatSum = (workHours) => {
  const hours = workHours.days() * 24 + workHours.hours()
  return toString(hours, workHours.minutes())
}

// input moment, output formated work hours
const formatWorkHours = (workHours) => (
  // to add negative sign
  toString(workHours.hours(), workHours.minutes())
)

// input hours and minutes, output string
const toString = (hours, minutes) => (
  `${hours} Hours and ${minutes} Minutes`
)

// input 'HH:mm', output moment object
const composeDateObject = (timeString) => {
  if (!timeString || timeString.length < 5) {
    return
  }
  const hour = timeString.split(':')[0]
  const minutes = timeString.split(':')[1]
  return moment({ hour, minutes })
}

// input start, end, pause
// output duration
const calculateWorkHours = (start, end, pause) => {
  // to assign hours to moment objects, we need the diff so current moment is fine
  const startDate = composeDateObject(start)
  const endDate = composeDateObject(end) || moment()
  const duration = moment.duration(endDate.diff(startDate.add({minutes: pause})))
  return duration
}

// to tell users when they can go home!
const shouldWorkUntil = (start, logger, CONFIG) => {
  const goHomeTime = composeDateObject(start)
    .add({hour: CONFIG.HOURS_IN_A_WORK_DAY})
    .add({minutes: CONFIG.BREAK_DEFAULT})
    .format('HH:mm')

  logger.info(
    '\n Working until ',
    goHomeTime,
    `will make it a full (${CONFIG.HOURS_IN_A_WORK_DAY}) day`
  )
}

// input obj. record
// output console.loggable table
const printSingleDayReport = (record) => {
// instantiate
  var table = new Table()

  table.push(
    { 'Today you worked:': record.dayReport },
    { 'Start:': record.start },
    { 'End:': record.end || 'Not set yet' },
    { 'Break duration:': record.breakDuration + ' minutes' },
    { 'Date:': record.date }
  )
  record.notes.forEach((note) => {
    if (!note) {
      return
    }
    table.push({'Note': note.createdat + ' ' + note.note})
  })
  return table.toString()
}

// full report of all days
const printAllDaysReport = (records, CONFIG) => {
  CONFIG = CONFIG || {}
  const pattern = CONFIG.DATE_FORMAT || 'YYYY-MM-DD'
  const table = buildTable(records, pattern)

  console.log('\n Report of all the days \n')
  console.log(table.toString())
}

// build a beautiful table
const buildTable = (records, pattern) => {
  let dayCounter = 0
  let sum
  let week = -1

  const table = new Table({
    head: [{colSpan: 2, content: 'Date'}, 'Work Hours'],
    style: {head: [], border: []},
    colWidths: [10, 15, 28]
  })

  records.forEach((record) => {
    const date = moment(record.date)
    const weekNumber = date.week()
    const workHours = record.workHours

    if (weekNumber > week) {
      // add sum if the week actually changes, but not at the beginning
      if (week > -1) {
        table.push(sumRow(sum, week))
      }
      sum = workHours
      dayCounter = 0
      week = weekNumber
    } else {
      sum.add(workHours)
      dayCounter += 1
    }

    table.push([{colSpan: 2, content: date.format(pattern)}, formatWorkHours(workHours)])
    addNotes(table, record)
  })
  // make sure there is a sum if it was not added before and we have more than one row
  if (dayCounter >= 0) {
    table.push(sumRow(sum, week))
  }
  return table
}

const addNotes = (table, record) => {
  record.notes && record.notes.forEach((note) => {
    table.push([{colSpan: 2, content: '  ' + note.createdat}, note.note])
  })
}

const sumRow = (sum, week) => (
  [{colSpan: 2, content: `Sum in week ${week}`}, formatSum(sum)]
)

module.exports = {
  composeDateObject,
  printSingleDayReport,
  printAllDaysReport,
  shouldWorkUntil,
  calculateWorkHours,
  formatWorkHours
}
<|endoftext|>