<commit_before>/* eslint */

'use strict';

// core modules
const path = require('path');

// dep modules
const _ = require('lodash');
const Promise = require('bluebird');
// const fs = require('fs-extra');

// own modules
// const HtmlParser = require('./HtmlParser');
// const Paths = require('./Paths');
const utils = require('./utils');
// const Debug = require('./Debug');
// const docmaPkg = require('../package.json');

// --------------------------------
// CLASS: DocmaTemplate
// --------------------------------

/**
 *  Class that provides template information and methods for supporting the
 *  documentation build process. An instance of this class is passed to the
 *  template module as the first argument.
 *  @class
 *  @memberof! Docma
 */
class DocmaTemplate {

    /**
     *  Initializes a new instance of `DocmaTemplate`.
     *  @private
     *  @hideconstructor
     *
     *  @param {Object} params
     *      @param {Object} params.modulePath - Resolved path of the template
     *      module.
     *      @param {Object} params.buildConfig - Docma build configuration (that
     *      also includes template configuration).
     *      @param {String} params.docmaVersion - Current Docma version.
     *      @param {Function} params.fnLog - Log function to be used within the
     *      template module.
     */
    constructor(params) {
        const dirname = path.dirname(params.path);
        const pkgPath = path.join(dirname, 'package.json');
        const pkg = utils.safeRequire(pkgPath);
        if (!pkg) {
            throw new Error(`Template package.json is not found at ${pkgPath}`);
        }
        pkg.docmaTemplate = pkg.docmaTemplate || {};

        /** @private */
        this._ = _.extend({}, params, {
            dirname,
            pkg,
            templateDir: path.join(dirname, 'template'),
            initialOptions: (params.buildConfig.template || {}).options || {},
            defaultOptions: null,
            mainHTML: '',
            compile: null,
            ignore: null
        });

        // explicitly execute the setter for defaultOptions
        this.defaultOptions = pkg.docmaTemplate.defaultOptions || {};

        // can be an object or string
        const author = typeof this._.pkg.author === 'string'
            ? this._.pkg.author
            : (this._.pkg.author || {}).name || '';
        // remove <email@email> part, if any.
        this._.author = author.replace(/\s*<.*?>/, '');

        /** @private */
        this._PreBuild = () => { };
        /** @private */
        this._postBuild = () => { };
    }

    get pkg() {
        return this._.pkg;
    }

    get name() {
        return this._.pkg.name;
    }

    get description() {
        return this._.pkg.description || '';
    }

    get version() {
        return this._.pkg.version;
    }

    get docmaVersion() {
        return this._.docmaVersion;
    }

    get supportedDocmaVersion() {
        return (this._.pkg.peerDependencies || {}).docma || '>=2.0.0';
    }

    get author() {
        return this._.author;
    }

    get license() {
        return this._.pkg.license || '';
    }

    get path() {
        return this._.path;
    }

    get dirname() {
        return this._.dirname;
    }

    get templateDir() {
        return this._.templateDir;
    }

    get buildConfig() {
        return this._.buildConfig || {};
    }

    get defaultOptions() {
        return this._.defaultOptions
            || this._.pkg.docmaTemplate.defaultOptions
            || {};
    }
    set defaultOptions(value) {
        this._.defaultOptions = value;
        this._.options = _.defaultsDeep(this._.initialOptions, this.defaultOptions);
    }

    get options() {
        return this._.options;
    }

    get mainHTML() {
        return this._.mainHTML
            || this._.pkg.docmaTemplate.mainHTML
            || 'index.html';
    }
    set mainHTML(value) {
        this._.mainHTML = value;
    }

    get ignore() {
        return this._.ignore
            || this._.pkg.docmaTemplate.ignore
            || [];
    }
    set ignore(value) {
        this._.ignore = value;
    }

    log(...args) {
        this._.fnLog(...args);
    }

    getSrcPath(...args) {
        return path.join(this.templateDir, ...args);
    }

    getDestPath(...args) {
        return path.join(this.buildConfig.dest, ...args);
    }

    preBuild(fn) {
        this._preBuild = typeof fn === 'function' ? fn : () => { };
    }

    postBuild(fn) {
        this._postBuild = typeof fn === 'function' ? fn : () => { };
    }

}

module.exports = DocmaTemplate;
<commit_msg>clean up<commit_after>/* eslint */

'use strict';

// core modules
const path = require('path');

// dep modules
const _ = require('lodash');

// own modules
const utils = require('./utils');

// --------------------------------
// CLASS: DocmaTemplate
// --------------------------------

/**
 *  Class that provides template information and methods for supporting the
 *  documentation build process. An instance of this class is passed to the
 *  template module as the first argument.
 *  @class
 *  @memberof! Docma
 */
class DocmaTemplate {

    /**
     *  Initializes a new instance of `DocmaTemplate`.
     *  @private
     *  @hideconstructor
     *
     *  @param {Object} params
     *      @param {Object} params.modulePath - Resolved path of the template
     *      module.
     *      @param {Object} params.buildConfig - Docma build configuration (that
     *      also includes template configuration).
     *      @param {String} params.docmaVersion - Current Docma version.
     *      @param {Function} params.fnLog - Log function to be used within the
     *      template module.
     */
    constructor(params) {
        const dirname = path.dirname(params.path);
        const pkgPath = path.join(dirname, 'package.json');
        const pkg = utils.safeRequire(pkgPath);
        if (!pkg) {
            throw new Error(`Template package.json is not found at ${pkgPath}`);
        }
        pkg.docmaTemplate = pkg.docmaTemplate || {};

        /** @private */
        this._ = _.extend({}, params, {
            dirname,
            pkg,
            templateDir: path.join(dirname, 'template'),
            initialOptions: (params.buildConfig.template || {}).options || {},
            defaultOptions: null,
            mainHTML: '',
            compile: null,
            ignore: null
        });

        // explicitly execute the setter for defaultOptions
        this.defaultOptions = pkg.docmaTemplate.defaultOptions || {};

        // can be an object or string
        const author = typeof this._.pkg.author === 'string'
            ? this._.pkg.author
            : (this._.pkg.author || {}).name || '';
        // remove <email@email> part, if any.
        this._.author = author.replace(/\s*<.*?>/, '');

        /** @private */
        this._PreBuild = () => { };
        /** @private */
        this._postBuild = () => { };
    }

    get pkg() {
        return this._.pkg;
    }

    get name() {
        return this._.pkg.name;
    }

    get description() {
        return this._.pkg.description || '';
    }

    get version() {
        return this._.pkg.version;
    }

    get docmaVersion() {
        return this._.docmaVersion;
    }

    get supportedDocmaVersion() {
        return (this._.pkg.peerDependencies || {}).docma || '>=2.0.0';
    }

    get author() {
        return this._.author;
    }

    get license() {
        return this._.pkg.license || '';
    }

    get path() {
        return this._.path;
    }

    get dirname() {
        return this._.dirname;
    }

    get templateDir() {
        return this._.templateDir;
    }

    get buildConfig() {
        return this._.buildConfig || {};
    }

    get defaultOptions() {
        return this._.defaultOptions
            || this._.pkg.docmaTemplate.defaultOptions
            || {};
    }
    set defaultOptions(value) {
        this._.defaultOptions = value;
        this._.options = _.defaultsDeep(this._.initialOptions, this.defaultOptions);
    }

    get options() {
        return this._.options;
    }

    get mainHTML() {
        return this._.mainHTML
            || this._.pkg.docmaTemplate.mainHTML
            || 'index.html';
    }
    set mainHTML(value) {
        this._.mainHTML = value;
    }

    get ignore() {
        return this._.ignore
            || this._.pkg.docmaTemplate.ignore
            || [];
    }
    set ignore(value) {
        this._.ignore = value;
    }

    log(...args) {
        this._.fnLog(...args);
    }

    getSrcPath(...args) {
        return path.join(this.templateDir, ...args);
    }

    getDestPath(...args) {
        return path.join(this.buildConfig.dest, ...args);
    }

    preBuild(fn) {
        this._preBuild = typeof fn === 'function' ? fn : () => { };
    }

    postBuild(fn) {
        this._postBuild = typeof fn === 'function' ? fn : () => { };
    }

}

module.exports = DocmaTemplate;
<|endoftext|>


<commit_before>import bytes from 'bytes';
import chalk from 'chalk';
import getFiles from './get-files';
import hash from './hash';
import retry from 'async-retry';
import Agent from './agent';
import EventEmitter from 'events';
import { basename, resolve } from 'path';
import { stat, readFile } from 'fs-promise';
import resumer from 'resumer';
import splitArray from 'split-array';

// how many concurrent HTTP/2 stream uploads
const MAX_CONCURRENT = 10;

// check if running windows
const IS_WIN = /^win/.test(process.platform);
const SEP = IS_WIN ? '\\' : '/';

export default class Now extends EventEmitter {
  constructor (url, token, { forceNew = false, debug = false }) {
    super();
    this._token = token;
    this._debug = debug;
    this._forceNew = forceNew;
    this._agent = new Agent(url, { debug });
    this._onRetry = this._onRetry.bind(this);
  }

  async create (path, { forceNew, forceSync }) {
    this._path = path;

    try {
      await stat(path);
    } catch (err) {
      const e = new Error(`Could not read directory ${path}.`);
      e.userError = true;
      throw e;
    }
    let pkg;
    try {
      pkg = await readFile(resolve(path, 'package.json'));
      pkg = JSON.parse(pkg);
    } catch (err) {
      const e = Error(`Failed to read JSON in "${path}/package.json"`);
      e.userError = true;
      throw e;
    }

    if (null == pkg.name || 'string' !== typeof pkg.name) {
      const e = Error('Missing or invalid `name` in `package.json`.');
      e.userError = true;
      throw e;
    }

    if (!pkg.scripts || (!pkg.scripts.start && !pkg.scripts['now-start'])) {
      const e = Error('Missing `start` (or `now-start`) script in `package.json`. ' +
        'See: https://docs.npmjs.com/cli/start.');
      e.userError = true;
      throw e;
    }

    if (this._debug) console.time('> [debug] Getting files');
    const files = await getFiles(path, pkg, { debug: this._debug });
    if (this._debug) console.timeEnd('> [debug] Getting files');

    if (this._debug) console.time('> [debug] Computing hashes');
    const hashes = await hash(files);
    if (this._debug) console.timeEnd('> [debug] Computing hashes');

    this._files = hashes;

    const engines = pkg['now-engines'];

    const deployment = await retry(async (bail) => {
      if (this._debug) console.time('> [debug] /now/create');
      const res = await this.fetch('/now/create', {
        method: 'POST',
        body: {
          forceNew,
          forceSync,
          name: pkg.name || basename(path),
          description: pkg.description,
          // Flatten the array to contain files to sync where each nested input
          // array has a group of files with the same sha but different path
          files: Array.prototype.concat.apply([], Array.from(this._files).map(([sha, { data, names }]) => {
            return names.map((name) => {
              return {
                sha,
                size: data.length,
                file: toRelative(name, this._path)
              };
            });
          })),
          engines
        }
      });
      if (this._debug) console.timeEnd('> [debug] /now/create');

      // no retry on 4xx
      if (200 !== res.status && (400 <= res.status && 500 > res.status)) {
        if (this._debug) {
          console.log('> [debug] bailing on creating due to %s', res.status);
        }
        return bail(responseError(res));
      }

      if (200 !== res.status) {
        throw new Error('Deployment initialization failed');
      }

      return res.json();
    }, { retries: 3, minTimeout: 2500, onRetry: this._onRetry });

    // we report about files whose sizes are too big
    if (deployment.warnings) {
      let sizeExceeded = 0;
      deployment.warnings.forEach((warning) => {
        if ('size_limit_exceeded' === warning.reason) {
          const { sha, limit } = warning;
          const name = hashes.get(sha).names.pop();
          console.error('> \u001b[31mWarning!\u001b[39m Skipping file %s (size exceeded %s)',
            name,
            bytes(limit)
          );
          hashes.get(sha).names.unshift(name); // move name (hack, if duplicate matches we report them in order)
          sizeExceeded++;
        } else if ('node_version_not_found' === warning.reason) {
          const { wanted, used } = warning;
          console.error('> \u001b[31mWarning!\u001b[39m Requested node version %s is not available, using default version %s',
            wanted,
            used
          );
        }
      });

      if (sizeExceeded) {
        console.log(`> \u001b[31mWarning!\u001b[39m ${sizeExceeded} of the files ` +
          'exceeded the limit for your plan.\n' +
          `> See ${chalk.underline('https://zeit.co/account')} to upgrade.`);
      }
    }

    this._id = deployment.deploymentId;
    this._host = deployment.url;
    this._missing = deployment.missing || [];

    return this._url;
  }

  upload () {
    const parts = splitArray(this._missing, MAX_CONCURRENT);

    if (this._debug) {
      console.log('> [debug] Will upload ' +
        `${this._missing.length} files in ${parts.length} ` +
        `steps of ${MAX_CONCURRENT} uploads.`);
    }

    const uploadChunk = () => {
      Promise.all(parts.shift().map((sha) => retry(async (bail, attempt) => {
        const file = this._files.get(sha);
        const { data, names } = file;

        if (this._debug) console.time(`> [debug] /sync #${attempt} ${names.join(' ')}`);
        const stream = resumer().queue(data).end();
        const res = await this.fetch('/now/sync', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/octet-stream',
            'Content-Length': data.length,
            'x-now-deployment-id': this._id,
            'x-now-sha': sha,
            'x-now-file': names.map((name) => toRelative(name, this._path)).join(','),
            'x-now-size': data.length
          },
          body: stream
        });
        if (this._debug) console.timeEnd(`> [debug] /sync #${attempt} ${names.join(' ')}`);

        // no retry on 4xx
        if (200 !== res.status && (400 <= res.status || 500 > res.status)) {
          if (this._debug) console.log('> [debug] bailing on creating due to %s', res.status);
          return bail(responseError(res));
        }

        this.emit('upload', file);
      }, { retries: 5, randomize: true, onRetry: this._onRetry })))
      .then(() => parts.length ? uploadChunk() : this.emit('complete'))
      .catch((err) => this.emit('error', err));
    };

    uploadChunk();
  }

  async list (app) {
    const query = app ? `?app=${encodeURIComponent(app)}` : '';

    const { deployments } = await retry(async (bail) => {
      if (this._debug) console.time('> [debug] /list');
      const res = await this.fetch('/now/list' + query);
      if (this._debug) console.timeEnd('> [debug] /list');

      // no retry on 4xx
      if (400 <= res.status && 500 > res.status) {
        if (this._debug) {
          console.log('> [debug] bailing on listing due to %s', res.status);
        }
        return bail(responseError(res));
      }

      if (200 !== res.status) {
        throw new Error('Fetching deployment list failed');
      }

      return res.json();
    }, { retries: 3, minTimeout: 2500, onRetry: this._onRetry });

    return deployments;
  }

  async remove (deploymentId, { hard }) {
    const data = { deploymentId, hard };

    await retry(async (bail) => {
      if (this._debug) console.time('> [debug] /remove');
      const res = await this.fetch('/now/remove', {
        method: 'DELETE',
        body: data
      });
      if (this._debug) console.timeEnd('> [debug] /remove');

      // no retry on 4xx
      if (400 <= res.status && 500 > res.status) {
        if (this._debug) {
          console.log('> [debug] bailing on removal due to %s', res.status);
        }
        return bail(responseError(res));
      }

      if (200 !== res.status) {
        throw new Error('Removing deployment failed');
      }
    }, { retries: 3, minTimeout: 2500, onRetry: this._onRetry });

    return true;
  }

  _onRetry (err) {
    if (this._debug) {
      console.log(`> [debug] Retrying: ${err.stack}`);
    }
  }

  close () {
    this._agent.close();
  }

  get id () {
    return this._id;
  }

  get url () {
    return `https://${this._host}`;
  }

  get host () {
    return this._host;
  }

  get syncAmount () {
    if (!this._syncAmount) {
      this._syncAmount = this._missing
      .map((sha) => this._files.get(sha).data.length)
      .reduce((a, b) => a + b, 0);
    }
    return this._syncAmount;
  }

  fetch (_url, opts = {}) {
    opts.headers = opts.headers || {};
    opts.headers.authorization = `Bearer ${this._token}`;
    return this._agent.fetch(_url, opts);
  }
}

function toRelative (path, base) {
  const fullBase = base.endsWith(SEP) ? base : base + SEP;
  let relative = path.substr(fullBase.length);
  if (relative.startsWith(SEP)) relative = relative.substr(1);
  return relative.replace(/\\/g, '/');
}

function responseError (res) {
  const err = new Error('Response error');
  err.status = res.status;

  if (429 === res.status) {
    const retryAfter = res.headers.get('Retry-After');
    if (retryAfter) {
      err.retryAfter = parseInt(retryAfter, 10);
    }
  }

  return err;
}
<commit_msg>index: restore `_fetch`<commit_after>import bytes from 'bytes';
import chalk from 'chalk';
import getFiles from './get-files';
import hash from './hash';
import retry from 'async-retry';
import Agent from './agent';
import EventEmitter from 'events';
import { basename, resolve } from 'path';
import { stat, readFile } from 'fs-promise';
import resumer from 'resumer';
import splitArray from 'split-array';

// how many concurrent HTTP/2 stream uploads
const MAX_CONCURRENT = 10;

// check if running windows
const IS_WIN = /^win/.test(process.platform);
const SEP = IS_WIN ? '\\' : '/';

export default class Now extends EventEmitter {
  constructor (url, token, { forceNew = false, debug = false }) {
    super();
    this._token = token;
    this._debug = debug;
    this._forceNew = forceNew;
    this._agent = new Agent(url, { debug });
    this._onRetry = this._onRetry.bind(this);
  }

  async create (path, { forceNew, forceSync }) {
    this._path = path;

    try {
      await stat(path);
    } catch (err) {
      const e = new Error(`Could not read directory ${path}.`);
      e.userError = true;
      throw e;
    }
    let pkg;
    try {
      pkg = await readFile(resolve(path, 'package.json'));
      pkg = JSON.parse(pkg);
    } catch (err) {
      const e = Error(`Failed to read JSON in "${path}/package.json"`);
      e.userError = true;
      throw e;
    }

    if (null == pkg.name || 'string' !== typeof pkg.name) {
      const e = Error('Missing or invalid `name` in `package.json`.');
      e.userError = true;
      throw e;
    }

    if (!pkg.scripts || (!pkg.scripts.start && !pkg.scripts['now-start'])) {
      const e = Error('Missing `start` (or `now-start`) script in `package.json`. ' +
        'See: https://docs.npmjs.com/cli/start.');
      e.userError = true;
      throw e;
    }

    if (this._debug) console.time('> [debug] Getting files');
    const files = await getFiles(path, pkg, { debug: this._debug });
    if (this._debug) console.timeEnd('> [debug] Getting files');

    if (this._debug) console.time('> [debug] Computing hashes');
    const hashes = await hash(files);
    if (this._debug) console.timeEnd('> [debug] Computing hashes');

    this._files = hashes;

    const engines = pkg['now-engines'];

    const deployment = await retry(async (bail) => {
      if (this._debug) console.time('> [debug] /now/create');
      const res = await this._fetch('/now/create', {
        method: 'POST',
        body: {
          forceNew,
          forceSync,
          name: pkg.name || basename(path),
          description: pkg.description,
          // Flatten the array to contain files to sync where each nested input
          // array has a group of files with the same sha but different path
          files: Array.prototype.concat.apply([], Array.from(this._files).map(([sha, { data, names }]) => {
            return names.map((name) => {
              return {
                sha,
                size: data.length,
                file: toRelative(name, this._path)
              };
            });
          })),
          engines
        }
      });
      if (this._debug) console.timeEnd('> [debug] /now/create');

      // no retry on 4xx
      if (200 !== res.status && (400 <= res.status && 500 > res.status)) {
        if (this._debug) {
          console.log('> [debug] bailing on creating due to %s', res.status);
        }
        return bail(responseError(res));
      }

      if (200 !== res.status) {
        throw new Error('Deployment initialization failed');
      }

      return res.json();
    }, { retries: 3, minTimeout: 2500, onRetry: this._onRetry });

    // we report about files whose sizes are too big
    if (deployment.warnings) {
      let sizeExceeded = 0;
      deployment.warnings.forEach((warning) => {
        if ('size_limit_exceeded' === warning.reason) {
          const { sha, limit } = warning;
          const name = hashes.get(sha).names.pop();
          console.error('> \u001b[31mWarning!\u001b[39m Skipping file %s (size exceeded %s)',
            name,
            bytes(limit)
          );
          hashes.get(sha).names.unshift(name); // move name (hack, if duplicate matches we report them in order)
          sizeExceeded++;
        } else if ('node_version_not_found' === warning.reason) {
          const { wanted, used } = warning;
          console.error('> \u001b[31mWarning!\u001b[39m Requested node version %s is not available, using default version %s',
            wanted,
            used
          );
        }
      });

      if (sizeExceeded) {
        console.log(`> \u001b[31mWarning!\u001b[39m ${sizeExceeded} of the files ` +
          'exceeded the limit for your plan.\n' +
          `> See ${chalk.underline('https://zeit.co/account')} to upgrade.`);
      }
    }

    this._id = deployment.deploymentId;
    this._host = deployment.url;
    this._missing = deployment.missing || [];

    return this._url;
  }

  upload () {
    const parts = splitArray(this._missing, MAX_CONCURRENT);

    if (this._debug) {
      console.log('> [debug] Will upload ' +
        `${this._missing.length} files in ${parts.length} ` +
        `steps of ${MAX_CONCURRENT} uploads.`);
    }

    const uploadChunk = () => {
      Promise.all(parts.shift().map((sha) => retry(async (bail, attempt) => {
        const file = this._files.get(sha);
        const { data, names } = file;

        if (this._debug) console.time(`> [debug] /sync #${attempt} ${names.join(' ')}`);
        const stream = resumer().queue(data).end();
        const res = await this._fetch('/now/sync', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/octet-stream',
            'Content-Length': data.length,
            'x-now-deployment-id': this._id,
            'x-now-sha': sha,
            'x-now-file': names.map((name) => toRelative(name, this._path)).join(','),
            'x-now-size': data.length
          },
          body: stream
        });
        if (this._debug) console.timeEnd(`> [debug] /sync #${attempt} ${names.join(' ')}`);

        // no retry on 4xx
        if (200 !== res.status && (400 <= res.status || 500 > res.status)) {
          if (this._debug) console.log('> [debug] bailing on creating due to %s', res.status);
          return bail(responseError(res));
        }

        this.emit('upload', file);
      }, { retries: 5, randomize: true, onRetry: this._onRetry })))
      .then(() => parts.length ? uploadChunk() : this.emit('complete'))
      .catch((err) => this.emit('error', err));
    };

    uploadChunk();
  }

  async list (app) {
    const query = app ? `?app=${encodeURIComponent(app)}` : '';

    const { deployments } = await retry(async (bail) => {
      if (this._debug) console.time('> [debug] /list');
      const res = await this._fetch('/now/list' + query);
      if (this._debug) console.timeEnd('> [debug] /list');

      // no retry on 4xx
      if (400 <= res.status && 500 > res.status) {
        if (this._debug) {
          console.log('> [debug] bailing on listing due to %s', res.status);
        }
        return bail(responseError(res));
      }

      if (200 !== res.status) {
        throw new Error('Fetching deployment list failed');
      }

      return res.json();
    }, { retries: 3, minTimeout: 2500, onRetry: this._onRetry });

    return deployments;
  }

  async remove (deploymentId, { hard }) {
    const data = { deploymentId, hard };

    await retry(async (bail) => {
      if (this._debug) console.time('> [debug] /remove');
      const res = await this._fetch('/now/remove', {
        method: 'DELETE',
        body: data
      });
      if (this._debug) console.timeEnd('> [debug] /remove');

      // no retry on 4xx
      if (400 <= res.status && 500 > res.status) {
        if (this._debug) {
          console.log('> [debug] bailing on removal due to %s', res.status);
        }
        return bail(responseError(res));
      }

      if (200 !== res.status) {
        throw new Error('Removing deployment failed');
      }
    }, { retries: 3, minTimeout: 2500, onRetry: this._onRetry });

    return true;
  }

  _onRetry (err) {
    if (this._debug) {
      console.log(`> [debug] Retrying: ${err.stack}`);
    }
  }

  close () {
    this._agent.close();
  }

  get id () {
    return this._id;
  }

  get url () {
    return `https://${this._host}`;
  }

  get host () {
    return this._host;
  }

  get syncAmount () {
    if (!this._syncAmount) {
      this._syncAmount = this._missing
      .map((sha) => this._files.get(sha).data.length)
      .reduce((a, b) => a + b, 0);
    }
    return this._syncAmount;
  }

  _fetch (_url, opts = {}) {
    opts.headers = opts.headers || {};
    opts.headers.authorization = `Bearer ${this._token}`;
    return this._agent.fetch(_url, opts);
  }
}

function toRelative (path, base) {
  const fullBase = base.endsWith(SEP) ? base : base + SEP;
  let relative = path.substr(fullBase.length);
  if (relative.startsWith(SEP)) relative = relative.substr(1);
  return relative.replace(/\\/g, '/');
}

function responseError (res) {
  const err = new Error('Response error');
  err.status = res.status;

  if (429 === res.status) {
    const retryAfter = res.headers.get('Retry-After');
    if (retryAfter) {
      err.retryAfter = parseInt(retryAfter, 10);
    }
  }

  return err;
}
<|endoftext|>


<commit_before>'use strict';
'use babel';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getPath = getPath;
const LOCAL_BIN_PATH = '/usr/local/bin';

function getPath() {
  if (process.platform !== 'darwin') {
    return process.env.PATH;
  }
  if (global.__STEELBRAIN_CONSISTENT_PATH) {
    return global.__STEELBRAIN_CONSISTENT_PATH;
  }
  const shellPath = require('shell-path');
  // Line copied from https://github.com/sindresorhus/fix-path/blob/master/index.js
  let path = shellPath.sync() || ['./node_modules/.bin', '/.nodebrew/current/bin', LOCAL_BIN_PATH, process.env.PATH].join(':');
  if (path.indexOf(LOCAL_BIN_PATH) === -1) {
    path += ':' + LOCAL_BIN_PATH;
  }
  global.__STEELBRAIN_CONSISTENT_PATH = path;
  return path;
}<commit_msg>:fire: Remove dist files from repo<commit_after><|endoftext|>


<commit_before>'use strict()';

var fs = require('fs'),
  exec = require('child_process').exec,
  os = require('os'),
  crontab = require('crontab'),
  username = require('username');

if (process.env.NODE_ENV === 'test') var test = true;

/**
 * Returns the path for the startup folder in Windows
 * @returns {String} path
 */
/* istanbul ignore next */
function getWinStartupPath() {
  var path;
  var majorRelease = os.release().substring(2, 0);
  majorRelease = majorRelease.replace('.', '');
  if (majorRelease >= 6) {
    path = 'C:\\Users\\' + username.sync() + '\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup';
  } else {
    path = 'C:\\Documents and Settings\\' + username.sync() + '\\Start Menu\\Programs\\Startup';
  }

  return path;
}

/**
 * Enables autostart
 * @param {String} key
 * @param {String} command
 * @param {String} path
 * @param {Function} callback
 */

function enableAutostart(key, command, path, callback) {
  var err = '';

  if (!key || !command || !path || !callback) {
    if (!test) console.error('Not enough arguments passed to node-autostart, enableAutostart().');
    return 1;
  }

  if (typeof(key) !== 'string') {
    if (!test) console.error('node-autostart: Passed "key" to enableAutostart() is not a string.');
    return 1;
  }

  if (typeof(command) !== 'string') {
    if (!test) console.error('node-autostart: Passed "command" to enableAutostart() is not a string.');
    return 1;
  }

  if (typeof(path) !== 'string') {
    if (!test) console.error('node-autostart: Passed "path" to enableAutostart() is not a string.');
    return 1;
  }

  if (typeof(callback) !== 'function') {
    if (!test) console.error('node-autostart: Passed "callback" to enableAutostart() is not a function.');
    return 1;
  }

  isAutostartEnabled(key, function(isEnabled, error) {
    if (error !== null) {
      err = error;
      callback(err);
      return 1;
    }

    /* istanbul ignore next */
    if (isEnabled === true) {
      err = 'Autostart already is enabled';
      callback(err);
      return 1;
    }

    /* istanbul ignore if  */
    if (os.platform() === 'darwin') {
      //Adding a service with the given key to launchd, by placing a launchd compatible .plist in the user's LaunchAgents folder
      /* istanbul ignore next */
      var plistFileContent = '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" \
      "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>Label</key><string>' + key + '\
      </string><key>ProgramArguments</key><array><string>bash</string><string>-c</string><string>cd ' + path + ' && ' + command + '\
      </string></array><key>RunAtLoad</key><true/></dict></plist>';

      fs.writeFile('/Users/' + username.sync() + '/Library/LaunchAgents/' + key + '.plist', plistFileContent, function(error) {
        err = error;

        if(err === '' || err === undefined) err = null;
        callback(err);
      });
    } /* istanbul ignore next */
    else if (os.platform() === 'linux') {
      var linuxCommand = 'cd ' + path + ' && ' + command;
      crontab.create(linuxCommand, '@reboot', key);

      crontab.save(function(error) {
        err = error;

        if (err === '' || err === undefined) err = null;
        callback(err);
      });
    } /* istanbul ignore next */
    else if (os.platform() === 'win32') {
      var batchFileContent; //File to be placed in the startup folder, will be generated here
      batchFileContent = 'cd ' + path + ' && ' + command;
      fs.writeFile(getWinStartupPath() + '/' + key + '.bat', batchFileContent, function(error) {
        err = error;

        if (err === '' || err === undefined) err = null;
        callback(err);
      });
    }
  });
}

/**
 * Disables autostart
 * @param {String} key
 * @param {Function} callback
 */

function disableAutostart(key, callback) {
  var err;

  if (!key || !callback) {
    if (!test) console.error('Not enough arguments passed to node-autostart, disableAutostart()');
    return 1;
  }

  if (typeof(key) !== 'string') {
    if (!test) console.error('node-autostart: Passed "key" to disableAutostart() is not a string.');
    return 1;
  }

  if (typeof(callback) !== 'function') {
    if (!test) console.error('node-autostart: Passed "callback" to disableAutostart() is not a function.');
    return 1;
  }

  isAutostartEnabled(key, function(isEnabled, error) {
    if (error !== null) {
      err = error;
      callback(err);
      return 1;
    }

    /* istanbul ignore next */
    if (!isEnabled) {
      err = 'Autostart is not enabled, so you cannot disable it';
      callback(err);
      return 1;
    }

    /* istanbul ignore if  */
    if (os.platform() === "darwin") {
      //Removing the launchd .plist with the name of the passed key
      /* istanbul ignore next */
      fs.unlink('/Users/' + username.sync() + '/Library/LaunchAgents/' + key + '.plist', function(error) {
        err = error;

        if (err === '' || err === undefined) err = null;
        callback(err);
      });
    } /* istanbul ignore next */
    else if (os.platform() === 'linux') {
      crontab.remove({comment:key});

      crontab.save(function(error) {
        err = error;

        if (err === '' || err === undefined) err = null;
        callback(err);
      });
    } /* istanbul ignore next */
    else if (os.platform() === 'win32') {
      fs.unlink(getWinStartupPath() + '/' + key + '.bat', function(error) {
        err = error;

        if (err === '' || err === undefined) err = null;
        callback(err);
      });
    }
  });
}

/**
 * Checks if autostart is enabled
 * @param {String} key
 * @param {Function} callback
 */

function isAutostartEnabled(key, callback) {
  var err;
  var isEnabled;

  if (!key || !callback) {
    if (!test) console.error('Not enough arguments passed to node-autostart, disableAutostart()');
    return 1;
  }

  if (typeof(key) !== 'string') {
    if (!test) console.error('node-autostart: Passed "key" to disableAutostart() is not a string.');
    return 1;
  }

  if (typeof(callback) !== 'function') {
    if (!test) console.error('node-autostart: Passed "callback" to disableAutostart() is not a function.');
    return 1;
  }

  /* istanbul ignore if  */
  if (os.platform() === "darwin") {
    /* istanbul ignore next */
    fs.stat('/Users/' + username.sync() + '/Library/LaunchAgents/' + key + '.plist', function(error, stat) {
      if (error === null) {
        isEnabled = true;
      } else if (error.code === 'ENOENT') {
        isEnabled = false;
      } else {
        err = error;
      }

      if (err === '' || err === undefined) err = null;
      callback(isEnabled, err);
    });
  } /* istanbul ignore next */
  else if (os.platform() === "linux") {
    var tmpLoad = crontab.load; //Variable for temporarily saving crontab.load() as crontab.load() overwrites 'crontab'
    crontab.load(function(error, crontabData) {
      crontab = crontabData; //Make crontab's data globally available
      crontab.load = tmpLoad;

      if(crontab.jobs({comment:key}).length == '0') {
        isEnabled = false;
      }
      else {
        isEnabled = true;
      }

      if (err === '' || err === undefined) err = null;
      callback(isEnabled, err);
    });
  } /* istanbul ignore next */
  else if (os.platform() === "win32") {
    fs.stat(getWinStartupPath() + '/' + key + '.bat', function(error, stat) {
      if (error === null) {
        isEnabled = true;
      } else if (error.code === 'ENOENT') {
        isEnabled = false;
      } else {
        err = error;
      }

      if (err === '' || err === undefined) err = null;
      callback(isEnabled, err);
    });
  }
  /* istanbul ignore else */
  else {
    err = 'Your platform currently is not supported';
    callback(isEnabled = false, err);
  }
}

module.exports = {
  enableAutostart: enableAutostart,
  disableAutostart: disableAutostart,
  isAutostartEnabled: isAutostartEnabled
};
<commit_msg>Updated index.js<commit_after>'use strict()';

var fs = require('fs'),
  exec = require('child_process').exec,
  os = require('os'),
  crontab = require('crontab'),
  username = require('username');

if (process.env.NODE_ENV === 'test') var test = true;

/**
 * Returns the path for the startup folder in Windows
 * @returns {String} path
 */
/* istanbul ignore next */
function getWinStartupPath() {
  var path;
  var majorRelease = os.release().substring(2, 0);
  majorRelease = majorRelease.replace('.', '');
  if (majorRelease >= 6) {
    path = 'C:\\Users\\' + username.sync() + '\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup';
  } else {
    path = 'C:\\Documents and Settings\\' + username.sync() + '\\Start Menu\\Programs\\Startup';
  }

  return path;
}

/**
 * Enables autostart
 * @param {String} key
 * @param {String} command
 * @param {String} path
 * @param {Function} callback
 */

function enableAutostart(key, command, path, callback) {
  var err = '';

  if (!key || !command || !path || !callback) {
    if (!test) console.error('Not enough arguments passed to node-autostart, enableAutostart().');
    return 1;
  }

  if (typeof(key) !== 'string') {
    if (!test) console.error('node-autostart: Passed "key" to enableAutostart() is not a string.');
    return 1;
  }

  if (typeof(command) !== 'string') {
    if (!test) console.error('node-autostart: Passed "command" to enableAutostart() is not a string.');
    return 1;
  }

  if (typeof(path) !== 'string') {
    if (!test) console.error('node-autostart: Passed "path" to enableAutostart() is not a string.');
    return 1;
  }

  if (typeof(callback) !== 'function') {
    if (!test) console.error('node-autostart: Passed "callback" to enableAutostart() is not a function.');
    return 1;
  }

  isAutostartEnabled(key, function(isEnabled, error) {
    /* istanbul ignore next */
    if (error !== null) {
      err = error;
      callback(err);
      return 1;
    }

    /* istanbul ignore next */
    if (isEnabled === true) {
      err = 'Autostart already is enabled';
      callback(err);
      return 1;
    }

    /* istanbul ignore next */
    if (os.platform() === 'darwin') {
      //Adding a service with the given key to launchd, by placing a launchd compatible .plist in the user's LaunchAgents folder
      /* istanbul ignore next */
      var plistFileContent = '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" \
      "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>Label</key><string>' + key + '\
      </string><key>ProgramArguments</key><array><string>bash</string><string>-c</string><string>cd ' + path + ' && ' + command + '\
      </string></array><key>RunAtLoad</key><true/></dict></plist>';

      fs.writeFile('/Users/' + username.sync() + '/Library/LaunchAgents/' + key + '.plist', plistFileContent, function(error) {
        err = error;

        if(err === '' || err === undefined) err = null;
        callback(err);
      });
    } /* istanbul ignore next */
    else if (os.platform() === 'linux') {
      var linuxCommand = 'cd ' + path + ' && ' + command;
      crontab.create(linuxCommand, '@reboot', key);

      crontab.save(function(error) {
        err = error;

        if (err === '' || err === undefined) err = null;
        callback(err);
      });
    } /* istanbul ignore next */
    else if (os.platform() === 'win32') {
      var batchFileContent; //File to be placed in the startup folder, will be generated here
      batchFileContent = 'cd ' + path + ' && ' + command;
      fs.writeFile(getWinStartupPath() + '/' + key + '.bat', batchFileContent, function(error) {
        err = error;

        if (err === '' || err === undefined) err = null;
        callback(err);
      });
    }
  });
}

/**
 * Disables autostart
 * @param {String} key
 * @param {Function} callback
 */

function disableAutostart(key, callback) {
  var err;

  if (!key || !callback) {
    if (!test) console.error('Not enough arguments passed to node-autostart, disableAutostart()');
    return 1;
  }

  if (typeof(key) !== 'string') {
    if (!test) console.error('node-autostart: Passed "key" to disableAutostart() is not a string.');
    return 1;
  }

  if (typeof(callback) !== 'function') {
    if (!test) console.error('node-autostart: Passed "callback" to disableAutostart() is not a function.');
    return 1;
  }

  isAutostartEnabled(key, function(isEnabled, error) {
    /* istanbul ignore next */
    if (error !== null) {
      err = error;
      callback(err);
      return 1;
    }

    /* istanbul ignore next */
    if (!isEnabled) {
      err = 'Autostart is not enabled, so you cannot disable it';
      callback(err);
      return 1;
    }

    /* istanbul ignore if  */
    if (os.platform() === "darwin") {
      //Removing the launchd .plist with the name of the passed key
      /* istanbul ignore next */
      fs.unlink('/Users/' + username.sync() + '/Library/LaunchAgents/' + key + '.plist', function(error) {
        err = error;

        if (err === '' || err === undefined) err = null;
        callback(err);
      });
    } /* istanbul ignore next */
    else if (os.platform() === 'linux') {
      crontab.remove({comment:key});

      crontab.save(function(error) {
        err = error;

        if (err === '' || err === undefined) err = null;
        callback(err);
      });
    } /* istanbul ignore next */
    else if (os.platform() === 'win32') {
      fs.unlink(getWinStartupPath() + '/' + key + '.bat', function(error) {
        err = error;

        if (err === '' || err === undefined) err = null;
        callback(err);
      });
    }
  });
}

/**
 * Checks if autostart is enabled
 * @param {String} key
 * @param {Function} callback
 */

function isAutostartEnabled(key, callback) {
  var err;
  var isEnabled;

  if (!key || !callback) {
    if (!test) console.error('Not enough arguments passed to node-autostart, disableAutostart()');
    return 1;
  }

  if (typeof(key) !== 'string') {
    if (!test) console.error('node-autostart: Passed "key" to disableAutostart() is not a string.');
    return 1;
  }

  if (typeof(callback) !== 'function') {
    if (!test) console.error('node-autostart: Passed "callback" to disableAutostart() is not a function.');
    return 1;
  }

  /* istanbul ignore if  */
  if (os.platform() === "darwin") {
    /* istanbul ignore next */
    fs.stat('/Users/' + username.sync() + '/Library/LaunchAgents/' + key + '.plist', function(error, stat) {
      if (error === null) {
        isEnabled = true;
      } else if (error.code === 'ENOENT') {
        isEnabled = false;
      } else {
        err = error;
      }

      if (err === '' || err === undefined) err = null;
      callback(isEnabled, err);
    });
  } /* istanbul ignore next */
  else if (os.platform() === "linux") {
    var tmpLoad = crontab.load; //Variable for temporarily saving crontab.load() as crontab.load() overwrites 'crontab'
    crontab.load(function(error, crontabData) {
      crontab = crontabData; //Make crontab's data globally available
      crontab.load = tmpLoad;

      if(crontab.jobs({comment:key}).length == '0') {
        isEnabled = false;
      }
      else {
        isEnabled = true;
      }

      if (err === '' || err === undefined) err = null;
      callback(isEnabled, err);
    });
  } /* istanbul ignore next */
  else if (os.platform() === "win32") {
    fs.stat(getWinStartupPath() + '/' + key + '.bat', function(error, stat) {
      if (error === null) {
        isEnabled = true;
      } else if (error.code === 'ENOENT') {
        isEnabled = false;
      } else {
        err = error;
      }

      if (err === '' || err === undefined) err = null;
      callback(isEnabled, err);
    });
  }
  /* istanbul ignore else */
  else {
    err = 'Your platform currently is not supported';
    callback(isEnabled = false, err);
  }
}

module.exports = {
  enableAutostart: enableAutostart,
  disableAutostart: disableAutostart,
  isAutostartEnabled: isAutostartEnabled
};
<|endoftext|>


<commit_before>/**
 * Parallel CasperJS runner, BDD flow, module-based tests, API helpers
 *
 * Licensed under MIT.
 * Copyright (c) 2013 David Smith <https://github.com/codeactual/>
 */

/*jshint node:true*/
/*global window:false, $:false, require:true*/
'use strict';

/**
 * Reference to Conjure.
 */
exports.Conjure = Conjure;

/**
 * Create a new Conjure.
 *
 * @param {object} requireCasper Casper-land `require()`
 * @return {object}
 */
exports.create = function(requireCasper) { return new Conjure(requireCasper); };

/**
 * Extend Conjure.prototype.
 *
 * @param {object} ext
 * @return {object} Merge result.
 */
exports.extendConjure = function(ext) { return require('extend')(Conjure.prototype, ext); };

/**
 * Extend the object that includes functions like helper.selectorExists.
 *
 * @param {object} ext
 * @return {object} Merge result.
 */
exports.extendHelpers = function(ext) { return require('extend')(helpers, ext); };

/**
 * Let tests load component-land modules.
 *
 * @type {function}
 * @api private
 */
exports.requireComponent = require;

/**
 * Let tests stub the component-land `require()`.
 *
 * @type {function}
 * @api private
 */
exports.setRequire = function(stub) { require = stub; };

var requireComponent = require;
var bddflow = require('bdd-flow');
var configurable = require('configurable.js');

/**
 * Add BDD globals and init configuration.
 *
 * Usage:
 *
 *     var conjure = require('conjure').create();
 *     conjure.set('exitOnError', false);
 *
 * Configuration:
 *
 * - `{string} baseUrl` Of target test server. `[http://localhost:8174]`
 * - `{string} initPath` Wait for this relative path to load before starting tests. `[/]`
 * - `{string} initSel` Wait for this selector (on `initPath`) before starting tests. `[body]`
 * - `{object} casperConfig` Native CasperJS `create()` settings.
 *
 * Default `casperConfig`:
 *
 *     {
 *       exitOnError: true,
 *       logLevel: 'debug',
 *       pageSettings: {
 *         loadImages: false,
 *         loadPlugins: false,
 *         XSSAuditingEnabled: true,
 *         verbose: true,
 *         onError: function(self, m) { self.die('CasperJS onError: ' + m, 1); },
 *         onLoadError: function(self, m) { self.die('CasperJS onLoadError: ' + m, 1); }
 *       }
 *     }
 *
 * To modify `casperConfig`:
 *
 * - `get() + set()` from a test script.
 * - Or apply globally using a `--bootstrap` module.
 *
 * Properties:
 *
 * - `{object} conjure` All `helpers` functions bound to `this`.
 * - `{object} flow` `Bddflow` instance
 * - `{object} utils` Native CasperJS `utils` module
 * - `{object} colorizer` Native CasperJS `colorizer` module
 * - `{boolean} running` True after Conjure.prototype.run executes
 *
 * @param {function} requireCasper CasperJS-env require()
 * @see Bddflow https://github.com/codeactual/bdd-flow/blob/master/docs/Bddflow.md
 */
function Conjure(requireCasper) {
  var self = this;
  var bind = requireComponent('bind');

  this.settings = {
    // Advertised.
    baseUrl: 'http://localhost:8174',
    initPath: '/',
    initSel: 'body',
    casperConfig: {
      exitOnError: true,
      logLevel: 'debug',
      pageSettings: {
        loadImages: false,
        loadPlugins: false,
        XSSAuditingEnabled: true,
        verbose: true,
        onError: function(self, m) { self.die('CasperJS onError: ' + m, 1); },
        onLoadError: function(self, m) { self.die('CasperJS onLoadError: ' + m, 1); }
      }
    },

    // Internal.
    cli: {}, // Native CasperJS CLI interface
    requireCasper: requireCasper // CasperJS-env require()
  };

  // TODO: is this needed?
  requireComponent('extend')(this, helpers);

  // Bound helpers. Added as properties so they can refer to others.
  this.conjure = {};
  Object.keys(helpers).forEach(function(key) {
    self.conjure[key] = bind(self, self[key]);
  });

  this.flow = bddflow.create();
  this.utils = this.require('utils');
  this.colorizer = this.require('colorizer').create('Colorizer');
  this.running = false;
}

configurable(Conjure.prototype);

/**
 * Build a context object that includes:
 *
 * - All enumerable keys from the parent.
 * - Where functions are bound to the parent.
 *
 * @param {object} parent
 * @param {string|array} pluck Key(s) from parent to pluck.
 * @param {string|array} omit Key(s) from parent to omit.
 * @return {object}
 */
Conjure.createContext = function(parent, pluck, omit) {
  var bind = require('bind');
  var each = require('each');
  var is = require('is');

  pluck = [].concat(pluck || []);
  omit = [].concat(omit || []);
  var context = {};
  var keys = pluck.length ? pluck : Object.keys(parent);
  each(keys, function(key) {
    if (-1 !== omit.indexOf(key)) { return; }
    if (is.Function(parent[key])) {
      context[key] = bind(parent, parent[key]);
    } else {
      context[key] = parent[key];
    }
  });
  return context;
};

/**
 * Check if (internal) run() has been called.
 *
 * @return {boolean}
 */
Conjure.prototype.isRunning = function() {
  return this.running;
};

/**
 * Run the suite/root `describe()`.
 *
 * Perform last-minute init based on collected configuration.
 * Silently add an initial describe() to verify initial URL/selector.
 *
 * @param {string} name Suite/root-`describe()` name
 * @param {function} cb
 */
Conjure.prototype.test = function(name, cb) {
  var self = this;

  var cli = this.get('cli');
  if (cli.options.grep) { // Convert `--grep[v] foo bar baz` to /foo bar baz/
    this.flow.set('grep', new RegExp(cli.args.join(' ')));
  } else if (cli.options.grepv) {
    this.flow.set('grepv', new RegExp(cli.args.join(' ')));
  }

  this.casper = this.require('casper').create(this.get('casperConfig'));
  this.flow.addContextProp('casper', this.casper);
  this.flow.addContextProp('colorizer', this.colorizer);
  this.flow.addContextProp('utils', this.utils);

  this.flow.set('itWrap', function(name, cb) {
    self.casper.then(function() {
      cb.call(this);
    });
  });
  this.flow.set('describeWrap', function(name, cb) {
    var contextKeys = ['casper', 'utils', 'colorizer', 'conjure'];
    cb.call(Conjure.createContext(self, contextKeys));
  });

  this.casper.start(this.url(this.get('initPath')));

  var descName = 'initial URL/selector';

  this.flow.addRootDescribe(descName, function() {
    this.it('should be loaded/found', function() {
      this.conjure.selectorExists(self.get('initSel'));
    });
  });

  this.flow.addRootDescribe(name, cb);
  this.run();
};

/**
 * Run collected BBD layers.
 */
Conjure.prototype.run = function() {
  var self = this;

  this.running = true;

  var initSel = this.get('initSel');
  var initPath = this.get('initPath');

  var initMsg = 'Opening [' + initPath + ']';
  if (initSel) {
    initMsg += ' Waiting For Selector [' + initSel + ']';
  }
  this.casper.test.info(initMsg);

  this.casper.then(function() {
    self.flow.run();
  });

  this.casper.run(function() {
    this.test.renderResults(true);
  });
};

/**
 * Send internal message to `conjure`.
 *
 * @param {string} source Ex. method name.
 * @param {string} type
 * - `wait` Details describe a potential timeout cause.
 * @param {object} detail Key/value pairs
 * @api private
 */
Conjure.prototype.status = function(source, type, detail) {
  detail = detail || {};
  detail.statusSource = source;
  console.log(this.utils.format(
    'conjure:%s:%s', type, JSON.stringify(detail)
  ));
};

/**
 * Methods mixed in to each it()/then() context.
 */
var helpers = {};

/**
 * click() alternative that uses jQuery selectors and first waits for a match.
 *
 * @param {string} sel
 */
helpers.click = function(sel) {
  this.conjure.selectorExists(sel);
  this.casper.thenEvaluate(function(sel) {
    $(sel).click();
  }, sel);
};

/**
 * then() alternative that with access to the same API as it().
 *
 * @param {function} cb
 */
helpers.then = function(cb) {
  var self = this;
  var extend = require('extend');
  var contextKeys = ['utils', 'colorizer', 'conjure'];
  var context = Conjure.createContext(this, contextKeys);
  this.casper.then(function() {
    cb.call(extend(context, {casper: self.casper, test: this.test}));
  });
};

/**
 * assertTextExists() alternative that uses jQuery selectors.
 *
 * @param {string} sel
 * @param {string|regexp} text
 */
helpers.assertSelText = function(sel, text) {
  var is = require('is');
  this.casper.then(function() {
    this.test['assert' + (is.string(text) ? 'Equals' : 'Match')](
      this.evaluate(function(sel) { return $(sel).text(); }, sel),
      text
    );
  });
};

/**
 * assertType() alternative that reveals the actual type on mismatch.
 *
 * @param {mixed} val
 * @param {string} expected Ex. 'number'
 * @param {string} subject Ex. 'user ID'
 */
helpers.assertType = function(val, expected, subject) {
  this.conjure.then(function() {
    this.test.assertEquals(
      this.utils.betterTypeOf(val),
      expected,
      this.utils.format('%s should be a %s', subject || 'subject', expected)
    );
  });
};

/**
 * casper.each() alternative executes the callback inside the custom then().
 * Callback receives the context of the enclosing then().
 *
 * @param {array} list
 * @param {function} cb Receives (listItem).
 */
helpers.each = function(list, cb) {
  var self = this;
  list.forEach(function(item) {
    self.conjure.then(function() {
      cb.call(this, item);
    });
  });
};

/**
 * Append a fragment ID to the current URL.
 *
 * @param {string} hash Without leading '#'.
 * @param {string} [sel] Optional selector to wait for after navigation.
 */
helpers.openHash = function(hash, sel) {
  this.casper.thenEvaluate(function _openHash(hash) {
    window.location.hash = '#' + hash;
  }, hash);
  if (sel) {
    this.conjure.selectorExists(sel);
  }
};

/**
 * Re-open the initial URL.
 */
helpers.openInitUrl = function() {
  this.casper.thenOpen(this.url(this.get('initPath')));
};

/**
 * require() a CasperJS module or any file relative to --rootdir.
 *
 * @param {string} name Ex. 'casper' or './relative/path/module.js'.
 *   For local file: prefix with leading './'.
 *     If rootdir is /path/to/proj, './foo' will lead to require('/path/to/proj/foo.js').
 * @return {mixed} Loaded module.
 */
helpers.require = function(name) {
  var require = this.get('requireCasper');
  var relPathRe = /^\.\//;
  if (relPathRe.test(name)) {
    return require(this.get('cli').options.rootdir + '/' + name.replace(relPathRe, ''));
  }
  return require(name); // Ex. 'casper' itself
};

/**
 * Alternative to waitForSelector() to use jQuery selector support,
 * ex. ':first' syntax.
 *
 * @param {string} sel
 * @param {boolean} [negate] Use true if selector is not expected to match.
 */
helpers.selectorExists = function(sel, negate) {
  var self = this;

  this.status(
    'selectorExists',
    'wait',
    {sel: sel, negate: negate}
  );

  this.casper.waitFor(function selectorExistsWaitFor() {
    return this.evaluate(function selectorExistsEvaluate(sel, count) {
      return count === $(sel).length;
    }, sel, negate ? 0 : 1);
  });
  this.casper.then(function selectorExistsThen() {
    this.test.assertTrue(true, (negate ? 'missing' : 'exists') + ': ' + sel);
  });
};

/**
 * Negated selectorExists().
 *
 * @param {string} sel
 */
helpers.selectorMissing = function(sel) {
  this.conjure.selectorExists(sel, true);
};

/**
 * sendKeys() alternative that first waits for a selector to exist.
 *
 * @param {string} sel
 * @param {string} keys
 */
helpers.sendKeys = function(sel, keys) {
  this.conjure.selectorExists(sel);
  this.conjure.then(function() {
    this.sendKeys(sel, keys);
  });
};

/**
 * Convert a relative URL into a full.
 *
 * @param {string} relUrl Includes leading slash.
 * @return {string}
 */
helpers.url = function(relUrl) {
  return this.get('baseUrl') + relUrl;
};
<commit_msg>docs(lib): Remove TODO<commit_after>/**
 * Parallel CasperJS runner, BDD flow, module-based tests, API helpers
 *
 * Licensed under MIT.
 * Copyright (c) 2013 David Smith <https://github.com/codeactual/>
 */

/*jshint node:true*/
/*global window:false, $:false, require:true*/
'use strict';

/**
 * Reference to Conjure.
 */
exports.Conjure = Conjure;

/**
 * Create a new Conjure.
 *
 * @param {object} requireCasper Casper-land `require()`
 * @return {object}
 */
exports.create = function(requireCasper) { return new Conjure(requireCasper); };

/**
 * Extend Conjure.prototype.
 *
 * @param {object} ext
 * @return {object} Merge result.
 */
exports.extendConjure = function(ext) { return require('extend')(Conjure.prototype, ext); };

/**
 * Extend the object that includes functions like helper.selectorExists.
 *
 * @param {object} ext
 * @return {object} Merge result.
 */
exports.extendHelpers = function(ext) { return require('extend')(helpers, ext); };

/**
 * Let tests load component-land modules.
 *
 * @type {function}
 * @api private
 */
exports.requireComponent = require;

/**
 * Let tests stub the component-land `require()`.
 *
 * @type {function}
 * @api private
 */
exports.setRequire = function(stub) { require = stub; };

var requireComponent = require;
var bddflow = require('bdd-flow');
var configurable = require('configurable.js');

/**
 * Add BDD globals and init configuration.
 *
 * Usage:
 *
 *     var conjure = require('conjure').create();
 *     conjure.set('exitOnError', false);
 *
 * Configuration:
 *
 * - `{string} baseUrl` Of target test server. `[http://localhost:8174]`
 * - `{string} initPath` Wait for this relative path to load before starting tests. `[/]`
 * - `{string} initSel` Wait for this selector (on `initPath`) before starting tests. `[body]`
 * - `{object} casperConfig` Native CasperJS `create()` settings.
 *
 * Default `casperConfig`:
 *
 *     {
 *       exitOnError: true,
 *       logLevel: 'debug',
 *       pageSettings: {
 *         loadImages: false,
 *         loadPlugins: false,
 *         XSSAuditingEnabled: true,
 *         verbose: true,
 *         onError: function(self, m) { self.die('CasperJS onError: ' + m, 1); },
 *         onLoadError: function(self, m) { self.die('CasperJS onLoadError: ' + m, 1); }
 *       }
 *     }
 *
 * To modify `casperConfig`:
 *
 * - `get() + set()` from a test script.
 * - Or apply globally using a `--bootstrap` module.
 *
 * Properties:
 *
 * - `{object} conjure` All `helpers` functions bound to `this`.
 * - `{object} flow` `Bddflow` instance
 * - `{object} utils` Native CasperJS `utils` module
 * - `{object} colorizer` Native CasperJS `colorizer` module
 * - `{boolean} running` True after Conjure.prototype.run executes
 *
 * @param {function} requireCasper CasperJS-env require()
 * @see Bddflow https://github.com/codeactual/bdd-flow/blob/master/docs/Bddflow.md
 */
function Conjure(requireCasper) {
  var self = this;
  var bind = requireComponent('bind');

  this.settings = {
    // Advertised.
    baseUrl: 'http://localhost:8174',
    initPath: '/',
    initSel: 'body',
    casperConfig: {
      exitOnError: true,
      logLevel: 'debug',
      pageSettings: {
        loadImages: false,
        loadPlugins: false,
        XSSAuditingEnabled: true,
        verbose: true,
        onError: function(self, m) { self.die('CasperJS onError: ' + m, 1); },
        onLoadError: function(self, m) { self.die('CasperJS onLoadError: ' + m, 1); }
      }
    },

    // Internal.
    cli: {}, // Native CasperJS CLI interface
    requireCasper: requireCasper // CasperJS-env require()
  };

  requireComponent('extend')(this, helpers);

  // Bound helpers. Added as properties so they can refer to others.
  this.conjure = {};
  Object.keys(helpers).forEach(function(key) {
    self.conjure[key] = bind(self, self[key]);
  });

  this.flow = bddflow.create();
  this.utils = this.require('utils');
  this.colorizer = this.require('colorizer').create('Colorizer');
  this.running = false;
}

configurable(Conjure.prototype);

/**
 * Build a context object that includes:
 *
 * - All enumerable keys from the parent.
 * - Where functions are bound to the parent.
 *
 * @param {object} parent
 * @param {string|array} pluck Key(s) from parent to pluck.
 * @param {string|array} omit Key(s) from parent to omit.
 * @return {object}
 */
Conjure.createContext = function(parent, pluck, omit) {
  var bind = require('bind');
  var each = require('each');
  var is = require('is');

  pluck = [].concat(pluck || []);
  omit = [].concat(omit || []);
  var context = {};
  var keys = pluck.length ? pluck : Object.keys(parent);
  each(keys, function(key) {
    if (-1 !== omit.indexOf(key)) { return; }
    if (is.Function(parent[key])) {
      context[key] = bind(parent, parent[key]);
    } else {
      context[key] = parent[key];
    }
  });
  return context;
};

/**
 * Check if (internal) run() has been called.
 *
 * @return {boolean}
 */
Conjure.prototype.isRunning = function() {
  return this.running;
};

/**
 * Run the suite/root `describe()`.
 *
 * Perform last-minute init based on collected configuration.
 * Silently add an initial describe() to verify initial URL/selector.
 *
 * @param {string} name Suite/root-`describe()` name
 * @param {function} cb
 */
Conjure.prototype.test = function(name, cb) {
  var self = this;

  var cli = this.get('cli');
  if (cli.options.grep) { // Convert `--grep[v] foo bar baz` to /foo bar baz/
    this.flow.set('grep', new RegExp(cli.args.join(' ')));
  } else if (cli.options.grepv) {
    this.flow.set('grepv', new RegExp(cli.args.join(' ')));
  }

  this.casper = this.require('casper').create(this.get('casperConfig'));
  this.flow.addContextProp('casper', this.casper);
  this.flow.addContextProp('colorizer', this.colorizer);
  this.flow.addContextProp('utils', this.utils);

  this.flow.set('itWrap', function(name, cb) {
    self.casper.then(function() {
      cb.call(this);
    });
  });
  this.flow.set('describeWrap', function(name, cb) {
    var contextKeys = ['casper', 'utils', 'colorizer', 'conjure'];
    cb.call(Conjure.createContext(self, contextKeys));
  });

  this.casper.start(this.url(this.get('initPath')));

  var descName = 'initial URL/selector';

  this.flow.addRootDescribe(descName, function() {
    this.it('should be loaded/found', function() {
      this.conjure.selectorExists(self.get('initSel'));
    });
  });

  this.flow.addRootDescribe(name, cb);
  this.run();
};

/**
 * Run collected BBD layers.
 */
Conjure.prototype.run = function() {
  var self = this;

  this.running = true;

  var initSel = this.get('initSel');
  var initPath = this.get('initPath');

  var initMsg = 'Opening [' + initPath + ']';
  if (initSel) {
    initMsg += ' Waiting For Selector [' + initSel + ']';
  }
  this.casper.test.info(initMsg);

  this.casper.then(function() {
    self.flow.run();
  });

  this.casper.run(function() {
    this.test.renderResults(true);
  });
};

/**
 * Send internal message to `conjure`.
 *
 * @param {string} source Ex. method name.
 * @param {string} type
 * - `wait` Details describe a potential timeout cause.
 * @param {object} detail Key/value pairs
 * @api private
 */
Conjure.prototype.status = function(source, type, detail) {
  detail = detail || {};
  detail.statusSource = source;
  console.log(this.utils.format(
    'conjure:%s:%s', type, JSON.stringify(detail)
  ));
};

/**
 * Methods mixed in to each it()/then() context.
 */
var helpers = {};

/**
 * click() alternative that uses jQuery selectors and first waits for a match.
 *
 * @param {string} sel
 */
helpers.click = function(sel) {
  this.conjure.selectorExists(sel);
  this.casper.thenEvaluate(function(sel) {
    $(sel).click();
  }, sel);
};

/**
 * then() alternative that with access to the same API as it().
 *
 * @param {function} cb
 */
helpers.then = function(cb) {
  var self = this;
  var extend = require('extend');
  var contextKeys = ['utils', 'colorizer', 'conjure'];
  var context = Conjure.createContext(this, contextKeys);
  this.casper.then(function() {
    cb.call(extend(context, {casper: self.casper, test: this.test}));
  });
};

/**
 * assertTextExists() alternative that uses jQuery selectors.
 *
 * @param {string} sel
 * @param {string|regexp} text
 */
helpers.assertSelText = function(sel, text) {
  var is = require('is');
  this.casper.then(function() {
    this.test['assert' + (is.string(text) ? 'Equals' : 'Match')](
      this.evaluate(function(sel) { return $(sel).text(); }, sel),
      text
    );
  });
};

/**
 * assertType() alternative that reveals the actual type on mismatch.
 *
 * @param {mixed} val
 * @param {string} expected Ex. 'number'
 * @param {string} subject Ex. 'user ID'
 */
helpers.assertType = function(val, expected, subject) {
  this.conjure.then(function() {
    this.test.assertEquals(
      this.utils.betterTypeOf(val),
      expected,
      this.utils.format('%s should be a %s', subject || 'subject', expected)
    );
  });
};

/**
 * casper.each() alternative executes the callback inside the custom then().
 * Callback receives the context of the enclosing then().
 *
 * @param {array} list
 * @param {function} cb Receives (listItem).
 */
helpers.each = function(list, cb) {
  var self = this;
  list.forEach(function(item) {
    self.conjure.then(function() {
      cb.call(this, item);
    });
  });
};

/**
 * Append a fragment ID to the current URL.
 *
 * @param {string} hash Without leading '#'.
 * @param {string} [sel] Optional selector to wait for after navigation.
 */
helpers.openHash = function(hash, sel) {
  this.casper.thenEvaluate(function _openHash(hash) {
    window.location.hash = '#' + hash;
  }, hash);
  if (sel) {
    this.conjure.selectorExists(sel);
  }
};

/**
 * Re-open the initial URL.
 */
helpers.openInitUrl = function() {
  this.casper.thenOpen(this.url(this.get('initPath')));
};

/**
 * require() a CasperJS module or any file relative to --rootdir.
 *
 * @param {string} name Ex. 'casper' or './relative/path/module.js'.
 *   For local file: prefix with leading './'.
 *     If rootdir is /path/to/proj, './foo' will lead to require('/path/to/proj/foo.js').
 * @return {mixed} Loaded module.
 */
helpers.require = function(name) {
  var require = this.get('requireCasper');
  var relPathRe = /^\.\//;
  if (relPathRe.test(name)) {
    return require(this.get('cli').options.rootdir + '/' + name.replace(relPathRe, ''));
  }
  return require(name); // Ex. 'casper' itself
};

/**
 * Alternative to waitForSelector() to use jQuery selector support,
 * ex. ':first' syntax.
 *
 * @param {string} sel
 * @param {boolean} [negate] Use true if selector is not expected to match.
 */
helpers.selectorExists = function(sel, negate) {
  var self = this;

  this.status(
    'selectorExists',
    'wait',
    {sel: sel, negate: negate}
  );

  this.casper.waitFor(function selectorExistsWaitFor() {
    return this.evaluate(function selectorExistsEvaluate(sel, count) {
      return count === $(sel).length;
    }, sel, negate ? 0 : 1);
  });
  this.casper.then(function selectorExistsThen() {
    this.test.assertTrue(true, (negate ? 'missing' : 'exists') + ': ' + sel);
  });
};

/**
 * Negated selectorExists().
 *
 * @param {string} sel
 */
helpers.selectorMissing = function(sel) {
  this.conjure.selectorExists(sel, true);
};

/**
 * sendKeys() alternative that first waits for a selector to exist.
 *
 * @param {string} sel
 * @param {string} keys
 */
helpers.sendKeys = function(sel, keys) {
  this.conjure.selectorExists(sel);
  this.conjure.then(function() {
    this.sendKeys(sel, keys);
  });
};

/**
 * Convert a relative URL into a full.
 *
 * @param {string} relUrl Includes leading slash.
 * @return {string}
 */
helpers.url = function(relUrl) {
  return this.get('baseUrl') + relUrl;
};
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var chalk = require('chalk');
var exit = require('exit');
var createHttpServer = require('./modules/http-server');
var createRewriteMiddleware = require('./modules/http/rewrite');
var createWsServer = require('./modules/ws-server');
var plugin = require('./modules/plugin');
var utils = require('./modules/utils');
var logMsg = utils.logMsg;
var logError = utils.logError;
var files = require('./modules/files');
var command = require('./command');
var buildinPlugins = fs.readdirSync(path.join(__dirname, 'plugins')).reduce(function(plugins, filename){
  var name = path.basename(filename, path.extname(filename));

  plugins[name] = {
    name: name,
    filename: path.join(__dirname, 'plugins', filename)
  };

  return plugins;
}, {});

exports.command = command;

//
// launched by another module
//
exports.launch = function(config){
  if (this === command)
    launch(config);

  if (this === exports)
    launch(command.normalize(config));
};

//
// launched directly (i.e. node index.js ..)
//
if (process.mainModule === module)
  command.run();

//
// main function
//
function launch(config){
  // settings
  var options = command.norm(config);

  var fsWatcher = options.sync ? require('./modules/watch').setBase(options.base) : {
    // stab fsWatcher
    awaitFile: function(){},
    startWatch: function(){},
    stopWatch: function(){},
    isFileObserve: function(){}
  };
  var httpServer = createHttpServer(options, fsWatcher);
  var rewrite = createRewriteMiddleware(options.rewrite);
  var plugins = [
    buildinPlugins.openFile
  ];

  utils.verbose = options.verbose;
  chalk.enabled = options.color && process.stdout.isTTY;
  files.setBase(options.base);

  // check base path
  if (!fs.existsSync(options.base))
  {
    console.error('Base path `' + options.base + '` not found');
    exit(2);
  }

  // banner
  if (options.verbose)
  {
    console.log('Base path: ' + chalk.green(options.base));
    console.log('Watching for FS: ' + chalk.green(options.sync ? 'YES' : 'NO'));
    console.log('Devtool: ' + (options.dev ? chalk.yellow('development') : chalk.green('build')));

    if (options.editor)
      console.log('Command to open file in editor: ' + chalk.green(options.editor + ' [filename]'));

    if (options.plugins.length)
      console.log('Plugins:\n  ' + options.plugins.map(function(pluginCfg){
        return chalk.green(pluginCfg.name) + ' → ' + chalk.gray(path.relative(process.cwd(), pluginCfg.filename).replace(/\\/g, '/'));
      }).join('\n  '));

    if (rewrite)
      rewrite.banner();

    console.log();
  }

  if (options.index)
    plugins.push(buildinPlugins.fileIndex);

  // init plugins
  plugins.concat(options.plugins).forEach(function(pluginCfg, index, array){
    var isBuildin = options.plugins.indexOf(pluginCfg) === -1;

    if (!isBuildin)
      logMsg('plugin', chalk.yellow(pluginCfg.name) + ' init', true);

    try {
      plugin.init(pluginCfg, options);
    } catch(e) {
      logError('plugin', chalk.yellow(pluginCfg.name) + '\n' + e.stack);
      exit(2);
    }

    if (!isBuildin)
    {
      if (options.verbose)
        logMsg('plugin', chalk.green(pluginCfg.name) + ' inited', true);
      else
        console.log('Plugin', chalk.green(pluginCfg.name), 'inited');

      // blank line after last item list
      if (index == array.length - 1)
        console.log('');
    }
  });

  //
  // files
  //

  files.onAdd(function(filename){
    logMsg('info', files.relativePath(filename) + ' ' + chalk.green('(add)'), true);
  });
  files.onRemove(function(filename){
    var file = files.get(filename);

    logMsg('info', files.relativePath(filename) + ' ' + chalk.red('(drop)'), true);

    fsWatcher.stopWatch(filename);
    if (file.notify)
      fsWatcher.awaitFile(filename);
  });
  files.onRead(function(err, filename, content){
    var file = files.get(filename);

    if (err)
      return logMsg('fs', 'Error: Can\'t read file ' + filename + ': ' + err);

    if (file.content !== content)
    {
      logMsg('info', files.relativePath(filename) + ' ' + chalk.yellow('(update content: ' + content.length + ' bytes)'), true);
      fsWatcher.startWatch(filename);
    }
  });

  //
  // create server
  //

  if (rewrite)
    httpServer.use(rewrite.middleware);

  httpServer.use(require('./modules/http/virtualPath').createMiddleware(httpServer));

  httpServer.listen(options.port, function(){
    var port = this.address().port;

    console.log('Server run at ' + chalk.green('http://localhost:' + port) + '\n');
  });

  //
  // Messaging and fs sync
  //

  if (options.sync)
  {
    var wsServer = createWsServer(httpServer, options);
    require('./modules/file-sync')(wsServer, httpServer, options);
    require('./modules/devtool')(wsServer, httpServer, options);
  }
}
<commit_msg>add fork method<commit_after>var fs = require('fs');
var path = require('path');
var chalk = require('chalk');
var exit = require('exit');
var createHttpServer = require('./modules/http-server');
var createRewriteMiddleware = require('./modules/http/rewrite');
var createWsServer = require('./modules/ws-server');
var plugin = require('./modules/plugin');
var utils = require('./modules/utils');
var logMsg = utils.logMsg;
var logError = utils.logError;
var files = require('./modules/files');
var command = require('./command');
var buildinPlugins = fs.readdirSync(path.join(__dirname, 'plugins')).reduce(function(plugins, filename){
  var name = path.basename(filename, path.extname(filename));

  plugins[name] = {
    name: name,
    filename: path.join(__dirname, 'plugins', filename)
  };

  return plugins;
}, {});

exports.command = command;

//
// run command in child process
//
exports.fork = function(args, options){
  return require('child_process').fork(__filename, args, options);
};

//
// launched by another module
//
exports.launch = function(config){
  if (this === command)
    launch(config);

  if (this === exports)
    launch(command.normalize(config));
};

//
// launched directly (i.e. node index.js ..)
//
if (process.mainModule === module)
  command.run();

//
// main function
//
function launch(config){
  // settings
  var options = command.norm(config);

  var fsWatcher = options.sync ? require('./modules/watch').setBase(options.base) : {
    // stab fsWatcher
    awaitFile: function(){},
    startWatch: function(){},
    stopWatch: function(){},
    isFileObserve: function(){}
  };
  var httpServer = createHttpServer(options, fsWatcher);
  var rewrite = createRewriteMiddleware(options.rewrite);
  var plugins = [
    buildinPlugins.openFile
  ];

  utils.verbose = options.verbose;
  chalk.enabled = options.color && process.stdout.isTTY;
  files.setBase(options.base);

  // check base path
  if (!fs.existsSync(options.base))
  {
    console.error('Base path `' + options.base + '` not found');
    exit(2);
  }

  // banner
  if (options.verbose)
  {
    console.log('Base path: ' + chalk.green(options.base));
    console.log('Watching for FS: ' + chalk.green(options.sync ? 'YES' : 'NO'));
    console.log('Devtool: ' + (options.dev ? chalk.yellow('development') : chalk.green('build')));

    if (options.editor)
      console.log('Command to open file in editor: ' + chalk.green(options.editor + ' [filename]'));

    if (options.plugins.length)
      console.log('Plugins:\n  ' + options.plugins.map(function(pluginCfg){
        return chalk.green(pluginCfg.name) + ' → ' + chalk.gray(path.relative(process.cwd(), pluginCfg.filename).replace(/\\/g, '/'));
      }).join('\n  '));

    if (rewrite)
      rewrite.banner();

    console.log();
  }

  if (options.index)
    plugins.push(buildinPlugins.fileIndex);

  // init plugins
  plugins.concat(options.plugins).forEach(function(pluginCfg, index, array){
    var isBuildin = options.plugins.indexOf(pluginCfg) === -1;

    if (!isBuildin)
      logMsg('plugin', chalk.yellow(pluginCfg.name) + ' init', true);

    try {
      plugin.init(pluginCfg, options);
    } catch(e) {
      logError('plugin', chalk.yellow(pluginCfg.name) + '\n' + e.stack);
      exit(2);
    }

    if (!isBuildin)
    {
      if (options.verbose)
        logMsg('plugin', chalk.green(pluginCfg.name) + ' inited', true);
      else
        console.log('Plugin', chalk.green(pluginCfg.name), 'inited');

      // blank line after last item list
      if (index == array.length - 1)
        console.log('');
    }
  });

  //
  // files
  //

  files.onAdd(function(filename){
    logMsg('info', files.relativePath(filename) + ' ' + chalk.green('(add)'), true);
  });
  files.onRemove(function(filename){
    var file = files.get(filename);

    logMsg('info', files.relativePath(filename) + ' ' + chalk.red('(drop)'), true);

    fsWatcher.stopWatch(filename);
    if (file.notify)
      fsWatcher.awaitFile(filename);
  });
  files.onRead(function(err, filename, content){
    var file = files.get(filename);

    if (err)
      return logMsg('fs', 'Error: Can\'t read file ' + filename + ': ' + err);

    if (file.content !== content)
    {
      logMsg('info', files.relativePath(filename) + ' ' + chalk.yellow('(update content: ' + content.length + ' bytes)'), true);
      fsWatcher.startWatch(filename);
    }
  });

  //
  // create server
  //

  if (rewrite)
    httpServer.use(rewrite.middleware);

  httpServer.use(require('./modules/http/virtualPath').createMiddleware(httpServer));

  httpServer.listen(options.port, function(){
    var port = this.address().port;

    console.log('Server run at ' + chalk.green('http://localhost:' + port) + '\n');
  });

  //
  // Messaging and fs sync
  //

  if (options.sync)
  {
    var wsServer = createWsServer(httpServer, options);
    require('./modules/file-sync')(wsServer, httpServer, options);
    require('./modules/devtool')(wsServer, httpServer, options);
  }
}
<|endoftext|>


<commit_before>
/*!
 * Ext JS Connect 0.0.1
 * Copyright(c) 2010 Ext JS, Inc.
 * MIT Licensed
 */

/**
 * Framework version.
 */

require('./proto');

exports.version = '0.0.1';

/**
 * Module dependencies.
 */

var sys = require('sys'),
    url = require('url'),
    path = require('path'),
    http = require('http');

// Response extensions

require('./response');

// chdir to CONNECT_CWD when present

if (process.env.CONNECT_CWD) {
    process.chdir(process.env.CONNECT_CWD);
}

// Runtime environment

var envName = process.env.CONNECT_ENV || 'development';

// Path to configuration module

var configPath = path.join(process.cwd(), 'config', envName);

// Connect environment configuration.

var env;
try {
    env = exports.env = require(configPath);
} catch (err) {
    env = exports.env = {};
}

// Defaults

env.name = envName;
env.port = env.port || process.env.CONNECT_SOCKET || 3000;

/**
 * Initialize a middleware Layer with the given
 * configuration object supporting the following keys:
 *
 *   - provider   name of provider middleware packaged with Connect
 *   - filter     name of filter middleware packaged with Connect
 *   - module     object responding to the "handle" method to provide your own middleware
 *   - param      single argument passed to the "setup" method when present
 *   - params     array of arguments passed to the "setup" method when present
 *
 * @param  {Object} config
 * @api private
 */

function Layer(config) {
    if (!(config.provider || config.filter || config.module)) {
        throw new Error("Must provide either 'provider', 'filter', or 'module' in layer config");
    }

    if (config.provider) {
        config.module = require("./providers/" + config.provider);
        this.name = config.provider;
    }
    else if (config.filter) {
        config.module = require("./filters/" + config.filter);
        this.name = config.filter;
    }

    if (config.module.name) {
        this.name = config.module.name;
    }

    if (config.param) {
        config.params = [config.param];
    }

    this.mixin(config.module);

    this.route = config.route || this.route;
    this.config = config;

    config.params = config.params || [];
    config.params.unshift(env);

    if (this.setup) {
        this.setup.apply(this, config.params);
    }
};

Layer.prototype = {
    name: 'unnamed',
    route: '/',

    /**
     * Default layer handle; warns about missing handle method,
     * and proceeds down the stack.
     */

    handle: function handle(req, res, next) {
        sys.debug("Warning: " + this.name + " middleware is missing handle");
        next(req, res);
    }
};

/**
 * Setup the given middleware configurations.
 *
 * The first value in each configuration array
 * may be an string or array of paths to match,
 * followed by the module object or name. All additional
 * arguments are passed to the optional setup function.
 *
 *   Connect.run(
 *      { filter: "log" },
 *      { provider: "static", param: __dirname + '/public' }
 *   )
 *
 * @param  {Array} middleware
 * @return {Server}
 * @api public
 */

function Server(configs) {
    var stack = this.stack = [];
    configs.forEach(function(config) {
        if (config.route instanceof Array) {
            config.route.forEach(function (route) {
                config.route = route;
                stack.push(new Layer(config));
            });
            return;
        }
        stack.push(new Layer(config));
    });

    // Call the parent constructor
    http.Server.call(this, this.handle);
}

sys.inherits(Server, http.Server);

/**
 * Listen on the given port number,
 * defaults to the current environment's port.
 *
 * @param  {Number} port
 * @return {Server}
 * @api public
 */

Server.prototype.listen = function listen(port) {
    this.port = port || env.port;
    sys.puts("Connect server listening on port " + this.port);
    http.Server.prototype.listen.call(this, this.port);
    return this;
};

/**
 * Handle server requests, punting them down
 * the middleware stack.
 *
 * @param  {ServerRequest} req
 * @param  {ServerResponse} res
 * @api private
 */

Server.prototype.handle = function handle(req, res, outerNext) {
    req.url = url.parse(req.url);

    var pathname = req.url.pathname,
        index = 0,
        stack = this.stack,
        scope = {};

    function next(req, res) {
        var layer = stack[index++];
        if (!layer) {
            if (outerNext) {
                return outerNext(req, res);
            }
            return res.simpleBody(404, "Cannot find " + pathname);
        }
        if (pathname.indexOf(layer.route) === 0) {
            return layer.handle(req, res, next, scope);
        }
        return next(res, req);
    }
    next(req, res);
};

/**
 * Shortcut for new connect.Server().
 *
 * @param  {Array} middleware
 * @return {Server}
 * @api public
 */

exports.createServer = function createServer(middleware){
    return new Server(middleware);
};

exports.Server = Server;<commit_msg>Moved proto require blog version<commit_after>
/*!
 * Ext JS Connect 0.0.1
 * Copyright(c) 2010 Ext JS, Inc.
 * MIT Licensed
 */

/**
 * Framework version.
 */

exports.version = '0.0.1';

require('./proto');

/**
 * Module dependencies.
 */

var sys = require('sys'),
    url = require('url'),
    path = require('path'),
    http = require('http');

// Response extensions

require('./response');

// chdir to CONNECT_CWD when present

if (process.env.CONNECT_CWD) {
    process.chdir(process.env.CONNECT_CWD);
}

// Runtime environment

var envName = process.env.CONNECT_ENV || 'development';

// Path to configuration module

var configPath = path.join(process.cwd(), 'config', envName);

// Connect environment configuration.

var env;
try {
    env = exports.env = require(configPath);
} catch (err) {
    env = exports.env = {};
}

// Defaults

env.name = envName;
env.port = env.port || process.env.CONNECT_SOCKET || 3000;

/**
 * Initialize a middleware Layer with the given
 * configuration object supporting the following keys:
 *
 *   - provider   name of provider middleware packaged with Connect
 *   - filter     name of filter middleware packaged with Connect
 *   - module     object responding to the "handle" method to provide your own middleware
 *   - param      single argument passed to the "setup" method when present
 *   - params     array of arguments passed to the "setup" method when present
 *
 * @param  {Object} config
 * @api private
 */

function Layer(config) {
    if (!(config.provider || config.filter || config.module)) {
        throw new Error("Must provide either 'provider', 'filter', or 'module' in layer config");
    }

    if (config.provider) {
        config.module = require("./providers/" + config.provider);
        this.name = config.provider;
    }
    else if (config.filter) {
        config.module = require("./filters/" + config.filter);
        this.name = config.filter;
    }

    if (config.module.name) {
        this.name = config.module.name;
    }

    if (config.param) {
        config.params = [config.param];
    }

    this.mixin(config.module);

    this.route = config.route || this.route;
    this.config = config;

    config.params = config.params || [];
    config.params.unshift(env);

    if (this.setup) {
        this.setup.apply(this, config.params);
    }
};

Layer.prototype = {
    name: 'unnamed',
    route: '/',

    /**
     * Default layer handle; warns about missing handle method,
     * and proceeds down the stack.
     */

    handle: function handle(req, res, next) {
        sys.debug("Warning: " + this.name + " middleware is missing handle");
        next(req, res);
    }
};

/**
 * Setup the given middleware configurations.
 *
 * The first value in each configuration array
 * may be an string or array of paths to match,
 * followed by the module object or name. All additional
 * arguments are passed to the optional setup function.
 *
 *   Connect.run(
 *      { filter: "log" },
 *      { provider: "static", param: __dirname + '/public' }
 *   )
 *
 * @param  {Array} middleware
 * @return {Server}
 * @api public
 */

function Server(configs) {
    var stack = this.stack = [];
    configs.forEach(function(config) {
        if (config.route instanceof Array) {
            config.route.forEach(function (route) {
                config.route = route;
                stack.push(new Layer(config));
            });
            return;
        }
        stack.push(new Layer(config));
    });

    // Call the parent constructor
    http.Server.call(this, this.handle);
}

sys.inherits(Server, http.Server);

/**
 * Listen on the given port number,
 * defaults to the current environment's port.
 *
 * @param  {Number} port
 * @return {Server}
 * @api public
 */

Server.prototype.listen = function listen(port) {
    this.port = port || env.port;
    sys.puts("Connect server listening on port " + this.port);
    http.Server.prototype.listen.call(this, this.port);
    return this;
};

/**
 * Handle server requests, punting them down
 * the middleware stack.
 *
 * @param  {ServerRequest} req
 * @param  {ServerResponse} res
 * @api private
 */

Server.prototype.handle = function handle(req, res, outerNext) {
    req.url = url.parse(req.url);

    var pathname = req.url.pathname,
        index = 0,
        stack = this.stack,
        scope = {};

    function next(req, res) {
        var layer = stack[index++];
        if (!layer) {
            if (outerNext) {
                return outerNext(req, res);
            }
            return res.simpleBody(404, "Cannot find " + pathname);
        }
        if (pathname.indexOf(layer.route) === 0) {
            return layer.handle(req, res, next, scope);
        }
        return next(res, req);
    }
    next(req, res);
};

/**
 * Shortcut for new connect.Server().
 *
 * @param  {Array} middleware
 * @return {Server}
 * @api public
 */

exports.createServer = function createServer(middleware){
    return new Server(middleware);
};

exports.Server = Server;<|endoftext|>


<commit_before>/** @module Node Pom Parser */

"use strict";

var fs = require("fs");
var xml2js = require("xml2js");
var util = require('util')
var traverse = require('traverse');

/**
 * Load xml file contents using the sync mode.
 * @param {object} opt Is the options with filePath in it.
 * @return {object} The xml content with timer with load time.
 */
function _loadXmlFileContents(opt) {
  if (!opt.filePath) {
    throw new Error("You must provide the opt.filePath");
  }

  return fs.readFileSync(opt.filePath, "utf8");
};

/**
 * Removes all the arrays with single elements with a string value.
 * @param {object} o is the object to be traversed.
 */
function removeSingleArrays(obj) {
  // Traverse all the elements of the object
  traverse(obj).forEach(function traversing(value) {
    // As the XML parser returns single fields as arrays.
    if (value instanceof Array && value.length === 1) {
      this.update(value[0]);
    }
  });
}

/**
 * Extremely FAST pom header reader using node-expat c library.
 * @param {object} opt Is the option with the filePath or xmlContent and the optional format.
 * @return {object} The pom object along with the timers.
 */
module.exports.parse = function(opt, callback) {
  if (!opt) {
    throw new Error("You must provide options: opt.filePath and any other option of " +
      "https://github.com/Leonidas-from-XIV/node-xml2js#options");
  }
  if (!opt.xmlContent && !opt.filePath) {
    throw new Error("You must provide the opt.filePath or the opt.xmlContent");
  }

  // Use or load the content from the filePath
  var loadedXml = false;
  if (!opt.xmlContent) {
    opt.xmlContent = _loadXmlFileContents(opt);
    loadedXml = true;
  }

  // xmljs options https://github.com/Leonidas-from-XIV/node-xml2js#options
  //opt.attrkey = "@";
  opt.trim = true;
  opt.normalizeTags = true;
  opt.normalize = true;
  opt.mergeAttrs = true;

  // parse the pom, erasing all
  xml2js.parseString(opt.xmlContent, opt, function (err, pomObject) {
    // Depending on the requested format, format it or not.
    if (err) {
      callback(err, null);
    }

    // Replace the arrays with single elements with strings
    removeSingleArrays(pomObject);

    var response = {};
    // Only add the pomXml when loaded from the file-system.
    if (loadedXml) {
      response.pomXml = opt.xmlContent;
    }
    // Always add the object
    response.pomObject = pomObject;

    // Callback with the value
    callback(null, response);
  });

};
<commit_msg>Issue #2: Refactoring the library with bluebird Promises API.<commit_after>/** @module Node Pom Parser */

"use strict";

var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));
var xml2js = require("xml2js");
var util = require('util')
var traverse = require('traverse');

// xmljs options https://github.com/Leonidas-from-XIV/node-xml2js#options
var XML2JS_OPTS = {
  trim: true,
  normalizeTags: true,
  normalize: true,
  mergeAttrs: true
};

/**
 * Parses xml into javascript object by using a file path or an xml content.
 * @param {object} opt Is the option with the filePath or xmlContent and the optional format.
 * @return {object} The pom object along with the timers.
 */
module.exports.parse = function(opt, callback) {
  if (!opt) {
    throw new Error("You must provide options: opt.filePath and any other option of " +
      "https://github.com/Leonidas-from-XIV/node-xml2js#options");
  }
  if (!opt.xmlContent && !opt.filePath) {
    throw new Error("You must provide the opt.filePath or the opt.xmlContent");
  }

  // If the xml content is was not provided by the api client.
  // https://github.com/petkaantonov/bluebird/blob/master/API.md#error-rejectedhandler----promise
  if (!opt.xmlContent) {
    fs.readFileAsync(opt.filePath, "utf8").then(function(xmlContent) {
      return xmlContent;

    }).then(_parseWithXml2js).then(function(result) {
      callback(null, result);

    }).catch(function(e) {
      callback(e, null);

    }).error(function (e) {
      callback(e, null);
    });

  } else {
    // parse the xml provided by the api client.
    _parseWithXml2js(opt.xmlContent).then(function(result) {
      delete result.xmlContent;
      callback(null, result);

    }).error(function (e) {
      callback(e);
    });
  }

};

/**
 * Parses the given xml content.
 * @param xmlContent {string} Is the xml content in string using utf-8 format.
 * @param loadedXml {boolean} Whether the xml was loaded from the file-system.
 * @param callback {function} The callback function using Javascript PCS.
 */
function _parseWithXml2js(xmlContent) {
  return new Promise(function(resolve, reject) {
    // parse the pom, erasing all
    xml2js.parseString(xmlContent, XML2JS_OPTS, function(err, pomObject) {
      if (err) {
        // Reject with the error
        reject(err);
      }

      // Replace the arrays with single elements with strings
      removeSingleArrays(pomObject);

      // Response to the call
      resolve({
        pomXml: xmlContent, // Only add the pomXml when loaded from the file-system.
        pomObject: pomObject // Always add the object
      });
    });
  });
}

/**
 * Removes all the arrays with single elements with a string value.
 * @param {object} o is the object to be traversed.
 */
function removeSingleArrays(obj) {
  // Traverse all the elements of the object
  traverse(obj).forEach(function traversing(value) {
    // As the XML parser returns single fields as arrays.
    if (value instanceof Array && value.length === 1) {
      this.update(value[0]);
    }
  });
}
<|endoftext|>