<commit_before>/*global describe, beforeEach, it*/
'use strict';

var path    = require('path');
var helpers = require('yeoman-generator').test;
var should  = require('should');

describe('clutch generator', function () {
  beforeEach(function (done) {
    helpers.testDirectory(path.join(__dirname, 'temp'), function (err) {
      if (err) return done(err);
      this.app = helpers.createGenerator('clutch:app', [
        '../../app'
      ]);
      done();
    }.bind(this));
  });

  it('creates expected files', function (done) {
    var _this = this;

    var expected = [
      'package.json', '.editorconfig', '.jshintrc', '.travis.yml'
    ];

    helpers.mockPrompt(this.app, {
      'githubUser': 'duro',
      'appName': 'test-app',
      'appDescription': 'This is a test app'
    });

    this.app.options['skip-install'] = true;
    this.app.run({}, function () {
      helpers.assertFiles(expected);
      done();
    });
  });
});
<commit_msg>Adding Gruntfile to tests<commit_after>/*global describe, beforeEach, it*/
'use strict';

var path    = require('path');
var helpers = require('yeoman-generator').test;
var should  = require('should');

describe('clutch generator', function () {
  beforeEach(function (done) {
    helpers.testDirectory(path.join(__dirname, 'temp'), function (err) {
      if (err) return done(err);
      this.app = helpers.createGenerator('clutch:app', [
        '../../app'
      ]);
      done();
    }.bind(this));
  });

  it('creates expected files', function (done) {
    var _this = this;

    var expected = [
      'package.json', '.editorconfig', '.jshintrc', '.travis.yml', 'Gruntfile.js'
    ];

    helpers.mockPrompt(this.app, {
      'githubUser': 'duro',
      'appName': 'test-app',
      'appDescription': 'This is a test app'
    });

    this.app.options['skip-install'] = true;
    this.app.run({}, function () {
      helpers.assertFiles(expected);
      done();
    });
  });
});
<|endoftext|>


<commit_before>'use strict'
const debug = require('debug')('webd2-api:model')
const _ = require('lodash')
const { NotFoundError } = require('jsonapi-errors/lib/errors')
const Serializer = require('jsonapi-serializer').Serializer
const Deserializer = require('jsonapi-serializer').Deserializer
const pluralize = require('pluralize')

const Schema = require('../sql-builder/Schema')
const {castTypes} = require('../utils/cast-types')
const camelCaseKeys = require('../utils/camel-case-keys')

class BaseModel {
  constructor (db, name, schema) {
    if (this.constructor === BaseModel) throw new TypeError('BaseModel is abstract class')

    if (!db) throw new Error('database is undefined')
    if (!name) throw new Error('name is undefined')

    this.db = db
    this.name = name

    if (schema && !(schema instanceof Object)) {
      throw new TypeError('schema attribute should be an object')
    }

    this.schema = new Schema(schema || {})

    this.serializer = new Serializer(name, {
      attributes: this.schema.attributes
    })

    this.deserializerOptions = {
      keyForAttribute: 'camelCase'
    }

    Object.keys(this.schema.relations).forEach((key) => {
      const relation = this.schema.relations[key]
      if (relation.belongsTo) {
        const pluralModelName = pluralize(relation.belongsTo.name)
        this.deserializerOptions[pluralModelName] = {
          valueForRelationship (rel) { return { id: rel.id } }
        }
      }
    })

    this.deserializer = new Deserializer(this.deserializerOptions)
  }

  sqlAll () { throw new Error('you should override sqlAll') }
  sqlOne () { throw new Error('you should override sqlOne') }
  sqlIsRowExist () { throw new Error('you should override sqlIsRowExist') }
  sqlUpdate () { throw new Error('you should override sqlUpdate') }
  sqlCreate () { throw new Error('you should override sqlCreate') }
  sqlDataWithID () { throw new Error('you should override sqlDataWithID') }

  /**
   * methods that return bare and did not type-cast records
   */
  all () {
    debug(`${this.name}:all`)
    return this.db.exec(this.sqlAll())
  }

  get (id) {
    if (id == null) throw new Error('no id has been provided')
    debug(`${this.name}:get${id}`)

    return this.db.exec(this.sqlOne(id))
    .then((rows) => {
      if (!rows || rows.length === 0) throw new NotFoundError('db returned no data')
      return rows[0]
    })
  }

  update (id, data) {
    if (id == null) throw new Error('no id has been provided')
    if (data == null) throw new Error('no data has been provided')
    debug(`${this.name}:update${id}`)

    return this.db.exec(this.sqlIsRowExist(id))
    .then((rows) => {
      if (rows.length === 0) throw new NotFoundError(`row with id: ${id} does not exist`)

      return this.db.exec(this.sqlUpdate(id, data))
      .then(() => this.get(id)) // if OK return new data
    })
  }

  create (data) {
    debug(`${this.name}:create`)

    return this.db.exec(this.sqlCreate(data))
    .then(() => {
      return this.db.exec(this.sqlDataWithID(data))
      .then((rows) => {
        if (rows.length === 0) {
          throw new NotFoundError(`something went wrong with INSERT ${JSON.stringify(data)}`)
        }
        return rows[0]
      })
    })
  }

  /**
   * methods that get and return JSONApi serialized data
   */
  fetchAll () {
    debug(`${this.name}:fetchAll`)

    return this.all()
    .then((records) => castTypes(records, this.schema))
    .then((castRecords) => this.serializer.serialize(castRecords))
    // .then((parentRows) => {
    //   return parentRows.map((parentRow) => {
    //     // parentRow[relationName] = _.find(castRelationRows, {'id': parentRow[fkName]})
    //     return parentRow
    //   })
    // })
  }

  /**
   * Serializer
   */
  serialize (records) {
    if (records == null) throw new Error('model.serialize() records cannot be undefined')

    return this.serializer.serialize(castTypes(records, this.schema))
  }

  /**
   * De-Serializer
   */
  deserialize (dataSet, cb) {
    this.deserializer.deserialize(dataSet, (err, data) => {
      if (err) return cb(err)

      // jsonapi-serializer@3.0.0 doesn't obey "keyForAttribute: 'camelCase'"
      camelCaseKeys(data)

      // return deserialized 'data' with camelCased keys
      cb(null, data)
    })
  }
}

module.exports = BaseModel
<commit_msg>use Schema.belongsToRelations<commit_after>'use strict'
const debug = require('debug')('webd2-api:model')
const _ = require('lodash')
const { NotFoundError } = require('jsonapi-errors/lib/errors')
const Serializer = require('jsonapi-serializer').Serializer
const Deserializer = require('jsonapi-serializer').Deserializer
const pluralize = require('pluralize')

const Schema = require('../sql-builder/Schema')
const {castTypes} = require('../utils/cast-types')
const camelCaseKeys = require('../utils/camel-case-keys')

class BaseModel {
  constructor (db, name, schema) {
    if (this.constructor === BaseModel) throw new TypeError('BaseModel is abstract class')

    if (!db) throw new Error('database is undefined')
    if (!name) throw new Error('name is undefined')

    this.db = db
    this.name = name

    if (schema && !(schema instanceof Object)) {
      throw new TypeError('schema attribute should be an object')
    }

    this.schema = new Schema(schema || {})

    this.serializer = new Serializer(name, {
      attributes: this.schema.attributes
    })

    this.deserializerOptions = {
      keyForAttribute: 'camelCase'
    }

    this.schema.belongsToRelations.forEach((relation) => {
      const pluralModelName = pluralize(relation.relationModel.name)
      this.deserializerOptions[pluralModelName] = {
        valueForRelationship (rel) { return { id: rel.id } }
      }
    })

    this.deserializer = new Deserializer(this.deserializerOptions)
  }

  sqlAll () { throw new Error('you should override sqlAll') }
  sqlOne () { throw new Error('you should override sqlOne') }
  sqlIsRowExist () { throw new Error('you should override sqlIsRowExist') }
  sqlUpdate () { throw new Error('you should override sqlUpdate') }
  sqlCreate () { throw new Error('you should override sqlCreate') }
  sqlDataWithID () { throw new Error('you should override sqlDataWithID') }

  /**
   * methods that return bare and did not type-cast records
   */
  all () {
    debug(`${this.name}:all`)
    return this.db.exec(this.sqlAll())
  }

  get (id) {
    if (id == null) throw new Error('no id has been provided')
    debug(`${this.name}:get${id}`)

    return this.db.exec(this.sqlOne(id))
    .then((rows) => {
      if (!rows || rows.length === 0) throw new NotFoundError('db returned no data')
      return rows[0]
    })
  }

  update (id, data) {
    if (id == null) throw new Error('no id has been provided')
    if (data == null) throw new Error('no data has been provided')
    debug(`${this.name}:update${id}`)

    return this.db.exec(this.sqlIsRowExist(id))
    .then((rows) => {
      if (rows.length === 0) throw new NotFoundError(`row with id: ${id} does not exist`)

      return this.db.exec(this.sqlUpdate(id, data))
      .then(() => this.get(id)) // if OK return new data
    })
  }

  create (data) {
    debug(`${this.name}:create`)

    return this.db.exec(this.sqlCreate(data))
    .then(() => {
      return this.db.exec(this.sqlDataWithID(data))
      .then((rows) => {
        if (rows.length === 0) {
          throw new NotFoundError(`something went wrong with INSERT ${JSON.stringify(data)}`)
        }
        return rows[0]
      })
    })
  }

  /**
   * methods that get and return JSONApi serialized data
   */
  fetchAll () {
    debug(`${this.name}:fetchAll`)

    return this.all()
    .then((records) => castTypes(records, this.schema))
    .then((castRecords) => this.serializer.serialize(castRecords))
    // .then((parentRows) => {
    //   return parentRows.map((parentRow) => {
    //     // parentRow[relationName] = _.find(castRelationRows, {'id': parentRow[fkName]})
    //     return parentRow
    //   })
    // })
  }

  /**
   * Serializer
   */
  serialize (records) {
    if (records == null) throw new Error('model.serialize() records cannot be undefined')

    return this.serializer.serialize(castTypes(records, this.schema))
  }

  /**
   * De-Serializer
   */
  deserialize (dataSet, cb) {
    this.deserializer.deserialize(dataSet, (err, data) => {
      if (err) return cb(err)

      // jsonapi-serializer@3.0.0 doesn't obey "keyForAttribute: 'camelCase'"
      camelCaseKeys(data)

      // return deserialized 'data' with camelCased keys
      cb(null, data)
    })
  }
}

module.exports = BaseModel
<|endoftext|>


<commit_before>/* eslint-env mocha */
'use strict'

const expect = require('chai').expect
const BlockService = require('ipfs-block-service')
const DAGService = require('ipfs-merkle-dag').DAGService
const UnixFS = require('ipfs-unixfs')
const fs = require('fs')
const path = require('path')
const bs58 = require('bs58')
const pull = require('pull-stream')
const zip = require('pull-zip')

const unixFSEngine = require('./../src')
const exporter = unixFSEngine.exporter

module.exports = (repo) => {
  describe('exporter', () => {
    let ds

    const bigFile = fs.readFileSync(path.join(__dirname, '/test-data/1.2MiB.txt'))
    before(() => {
      const bs = new BlockService(repo)
      ds = new DAGService(bs)
    })

    it('ensure hash inputs are sanitized', (done) => {
      const hash = 'QmQmZQxSKQppbsWfVzBvg59Cn3DKtsNVQ94bjAxg2h3Lb8'
      const mhBuf = new Buffer(bs58.decode(hash))

      pull(
        ds.getStream(hash),
        pull.map((node) => UnixFS.unmarshal(node.data)),
        pull.collect((err, nodes) => {
          expect(err).to.not.exist

          const unmarsh = nodes[0]

          pull(
            exporter(mhBuf, ds),
            pull.collect(onFiles)
          )

          function onFiles (err, files) {
            expect(err).to.not.exist
            expect(files).to.have.length(1)
            expect(files[0]).to.have.property('path', hash)

            fileEql(files[0], unmarsh.data, done)
          }
        })
      )
    })

    it('export a file with no links', (done) => {
      const hash = 'QmQmZQxSKQppbsWfVzBvg59Cn3DKtsNVQ94bjAxg2h3Lb8'

      pull(
        zip(
          pull(
            ds.getStream(hash),
            pull.map((node) => UnixFS.unmarshal(node.data))
          ),
          exporter(hash, ds)
        ),
        pull.collect((err, values) => {
          expect(err).to.not.exist
          const unmarsh = values[0][0]
          const file = values[0][1]

          fileEql(file, unmarsh.data, done)
        })
      )
    })

    it('export a small file with links', (done) => {
      const hash = 'QmW7BDxEbGqxxSYVtn3peNPQgdDXbWkoQ6J1EFYAEuQV3Q'
      pull(
        exporter(hash, ds),
        pull.collect((err, files) => {
          expect(err).to.not.exist

          fileEql(files[0], bigFile, done)
        })
      )
    })

    it('export a large file > 5mb', (done) => {
      const hash = 'QmRQgufjp9vLE8XK2LGKZSsPCFCF6e4iynCQtNB5X2HBKE'
      pull(
        exporter(hash, ds),
        pull.collect((err, files) => {
          expect(err).to.not.exist

          expect(files[0]).to.have.property('path', 'QmRQgufjp9vLE8XK2LGKZSsPCFCF6e4iynCQtNB5X2HBKE')
          fileEql(files[0], null, done)
        })
      )
    })

    it('export a directory', (done) => {
      const hash = 'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKjN'

      pull(
        exporter(hash, ds),
        pull.collect((err, files) => {
          expect(err).to.not.exist

          expect(
            files.map((file) => file.path)
          ).to.be.eql([
            'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKjN',
            'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKjN/200Bytes.txt',
            'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKjN/dir-another',
            'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKjN/level-1',
            'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKjN/level-1/200Bytes.txt',
            'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKjN/level-1/level-2'
          ])

          pull(
            pull.values(files),
            pull.map((file) => Boolean(file.content)),
            pull.collect((err, contents) => {
              expect(err).to.not.exist
              expect(contents).to.be.eql([
                false,
                true,
                false,
                false,
                true,
                false
              ])
              done()
            })
          )
        })
      )
    })

    it('returns an empty stream for dir', (done) => {
      const hash = 'QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn'

      pull(
        exporter(hash, ds),
        pull.collect((err, files) => {
          expect(err).to.not.exist
          expect(files[0].content).to.not.exist
          done()
        })
      )
    })

    it('fails on non existent hash', (done) => {
      // This hash doesn't exist in the repo
      const hash = 'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKj3'

      pull(
        exporter(hash, ds),
        pull.collect((err, files) => {
          expect(err).to.exist
          done()
        })
      )
    })
  })
}

function fileEql (f1, f2, done) {
  pull(
    f1.content,
    pull.collect((err, data) => {
      if (err) return done(err)

      try {
        if (f2) {
          expect(Buffer.concat(data)).to.be.eql(f2)
        } else {
          expect(data).to.exist
        }
      } catch (err) {
        return done(err)
      }
      done()
    })
  )
}
<commit_msg>test: import - export with size check<commit_after>/* eslint-env mocha */
'use strict'

const expect = require('chai').expect
const BlockService = require('ipfs-block-service')
const DAGService = require('ipfs-merkle-dag').DAGService
const UnixFS = require('ipfs-unixfs')
const fs = require('fs')
const path = require('path')
const bs58 = require('bs58')
const pull = require('pull-stream')
const zip = require('pull-zip')

const unixFSEngine = require('./../src')
const exporter = unixFSEngine.exporter

module.exports = (repo) => {
  describe('exporter', () => {
    let ds

    const bigFile = fs.readFileSync(path.join(__dirname, '/test-data/1.2MiB.txt'))
    before(() => {
      const bs = new BlockService(repo)
      ds = new DAGService(bs)
    })

    it('import and export', (done) => {
      pull(
        pull.values([{
          path: '1.2MiB.txt',
          content: pull.values([bigFile, Buffer('hello world')])
        }]),
        unixFSEngine.importer(ds),
        pull.map((file) => {
          expect(file.path).to.be.eql('1.2MiB.txt')

          return exporter(file.multihash, ds)
        }),
        pull.flatten(),
        pull.collect((err, files) => {
          expect(err).to.not.exist
          expect(files[0].size).to.be.eql(bigFile.length + 11)
          fileEql(files[0], Buffer.concat([bigFile, Buffer('hello world')]), done)
        })
      )
    })

    it('ensure hash inputs are sanitized', (done) => {
      const hash = 'QmQmZQxSKQppbsWfVzBvg59Cn3DKtsNVQ94bjAxg2h3Lb8'
      const mhBuf = new Buffer(bs58.decode(hash))

      pull(
        ds.getStream(hash),
        pull.map((node) => UnixFS.unmarshal(node.data)),
        pull.collect((err, nodes) => {
          expect(err).to.not.exist

          const unmarsh = nodes[0]

          pull(
            exporter(mhBuf, ds),
            pull.collect(onFiles)
          )

          function onFiles (err, files) {
            expect(err).to.not.exist
            expect(files).to.have.length(1)
            expect(files[0]).to.have.property('path', hash)

            fileEql(files[0], unmarsh.data, done)
          }
        })
      )
    })

    it('export a file with no links', (done) => {
      const hash = 'QmQmZQxSKQppbsWfVzBvg59Cn3DKtsNVQ94bjAxg2h3Lb8'

      pull(
        zip(
          pull(
            ds.getStream(hash),
            pull.map((node) => UnixFS.unmarshal(node.data))
          ),
          exporter(hash, ds)
        ),
        pull.collect((err, values) => {
          expect(err).to.not.exist
          const unmarsh = values[0][0]
          const file = values[0][1]

          fileEql(file, unmarsh.data, done)
        })
      )
    })

    it('export a small file with links', (done) => {
      const hash = 'QmW7BDxEbGqxxSYVtn3peNPQgdDXbWkoQ6J1EFYAEuQV3Q'
      pull(
        exporter(hash, ds),
        pull.collect((err, files) => {
          expect(err).to.not.exist

          fileEql(files[0], bigFile, done)
        })
      )
    })

    it('export a large file > 5mb', (done) => {
      const hash = 'QmRQgufjp9vLE8XK2LGKZSsPCFCF6e4iynCQtNB5X2HBKE'
      pull(
        exporter(hash, ds),
        pull.collect((err, files) => {
          expect(err).to.not.exist

          expect(files[0]).to.have.property('path', 'QmRQgufjp9vLE8XK2LGKZSsPCFCF6e4iynCQtNB5X2HBKE')
          fileEql(files[0], null, done)
        })
      )
    })

    it('export a directory', (done) => {
      const hash = 'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKjN'

      pull(
        exporter(hash, ds),
        pull.collect((err, files) => {
          expect(err).to.not.exist

          expect(
            files.map((file) => file.path)
          ).to.be.eql([
            'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKjN',
            'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKjN/200Bytes.txt',
            'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKjN/dir-another',
            'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKjN/level-1',
            'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKjN/level-1/200Bytes.txt',
            'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKjN/level-1/level-2'
          ])

          pull(
            pull.values(files),
            pull.map((file) => Boolean(file.content)),
            pull.collect((err, contents) => {
              expect(err).to.not.exist
              expect(contents).to.be.eql([
                false,
                true,
                false,
                false,
                true,
                false
              ])
              done()
            })
          )
        })
      )
    })

    it('returns an empty stream for dir', (done) => {
      const hash = 'QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn'

      pull(
        exporter(hash, ds),
        pull.collect((err, files) => {
          expect(err).to.not.exist
          expect(files[0].content).to.not.exist
          done()
        })
      )
    })

    it('fails on non existent hash', (done) => {
      // This hash doesn't exist in the repo
      const hash = 'QmWChcSFMNcFkfeJtNd8Yru1rE6PhtCRfewi1tMwjkwKj3'

      pull(
        exporter(hash, ds),
        pull.collect((err, files) => {
          expect(err).to.exist
          done()
        })
      )
    })
  })
}

function fileEql (f1, f2, done) {
  pull(
    f1.content,
    pull.collect((err, data) => {
      if (err) return done(err)

      try {
        if (f2) {
          expect(Buffer.concat(data)).to.be.eql(f2)
        } else {
          expect(data).to.exist
        }
      } catch (err) {
        return done(err)
      }
      done()
    })
  )
}
<|endoftext|>


<commit_before>/** @babel */
import path from 'path';

import {Emitter} from 'atom';

import GitShellOutStrategy from '../git-shell-out-strategy';
import FilePatch from './file-patch';
import Hunk from './hunk';
import HunkLine from './hunk-line';
import {readFile} from '../helpers';

const MERGE_MARKER_REGEX = /^(>|<){7} \S+$/m;

export class AbortMergeError extends Error {
  constructor(code, filePath) {
    super();
    this.message = `${code}: ${filePath}.`;
    this.code = code;
    this.path = filePath;
    this.stack = new Error().stack;
  }
}

export class CommitError extends Error {
  constructor(code) {
    super();
    this.message = `Commit error: ${code}.`;
    this.code = code;
    this.stack = new Error().stack;
  }
}

export default class Repository {
  static async open(workingDirectory, gitStrategy) {
    const strategy = gitStrategy || new GitShellOutStrategy(workingDirectory.getPath());
    if (await strategy.isGitRepository(workingDirectory)) {
      return new Repository(workingDirectory, strategy);
    } else {
      return null;
    }
  }

  constructor(workingDirectory, gitStrategy) {
    this.workingDirectory = workingDirectory;
    this.emitter = new Emitter();
    this.stagedFilePatchesByPath = new Map();
    this.stagedFilePatchesSinceParentCommitByPath = new Map();
    this.unstagedFilePatchesByPath = new Map();
    this.git = gitStrategy;
    this.destroyed = false;
  }

  destroy() {
    this.destroyed = true;
    this.emitter.emit('did-destroy');
    this.emitter.dispose();
  }

  isDestroyed() {
    return this.destroyed;
  }

  onDidDestroy(callback) {
    return this.emitter.on('did-destroy', callback);
  }

  onDidUpdate(callback) {
    return this.emitter.on('did-update', callback);
  }

  didUpdate() {
    this.emitter.emit('did-update');
  }

  getWorkingDirectory() {
    return this.workingDirectory;
  }

  getWorkingDirectoryPath() {
    return this.getWorkingDirectory().getRealPathSync();
  }

  getGitDirectoryPath() {
    return path.join(this.getWorkingDirectoryPath(), '.git');
  }

  async refresh() {
    /* eslint-disable no-console */
    this.clearCachedFilePatches();
    if (process.env.PRINT_GIT_TIMES) { console.time('refresh'); }
    this.fileStatusesPromise = this.fetchStatusesForChangedFiles();
    this.stagedChangesSinceParentCommitPromise = this.fetchStagedChangesSinceParentCommit();
    await Promise.all([
      this.fileStatusesPromise,
      this.stagedChangesSinceParentCommitPromise,
    ]);
    if (process.env.PRINT_GIT_TIMES) { console.timeEnd('refresh'); }
    this.didUpdate();
    /* eslint-enable no-console */
  }

  fetchStatusesForChangedFiles() {
    return this.git.getStatusesForChangedFiles();
  }

  getStatusesForChangedFiles() {
    if (!this.fileStatusesPromise) {
      this.fileStatusesPromise = this.fetchStatusesForChangedFiles();
    }
    return this.fileStatusesPromise;
  }

  async getUnstagedChanges() {
    const {unstagedFiles} = await this.getStatusesForChangedFiles();
    return Object.keys(unstagedFiles).map(filePath => { return {filePath, status: unstagedFiles[filePath]}; });
  }

  async getStagedChanges() {
    const {stagedFiles} = await this.getStatusesForChangedFiles();
    return Object.keys(stagedFiles).map(filePath => { return {filePath, status: stagedFiles[filePath]}; });
  }

  async getMergeConflicts() {
    const {mergeConflictFiles} = await this.getStatusesForChangedFiles();
    return Object.keys(mergeConflictFiles).map(filePath => {
      return {filePath, status: mergeConflictFiles[filePath]};
    });
  }

  async fetchStagedChangesSinceParentCommit() {
    try {
      const stagedFiles = await this.git.diffFileStatus({staged: true, target: 'HEAD~'});
      return Object.keys(stagedFiles).map(filePath => { return {filePath, status: stagedFiles[filePath]}; });
    } catch (e) {
      if (e.message.includes('ambiguous argument \'HEAD~\'')) {
        return [];
      } else {
        throw e;
      }
    }
  }

  getStagedChangesSinceParentCommit() {
    if (!this.stagedChangesSinceParentCommitPromise) {
      this.stagedChangesSinceParentCommitPromise = this.fetchStagedChangesSinceParentCommit();
    }
    return this.stagedChangesSinceParentCommitPromise;
  }

  clearCachedFilePatches() {
    this.stagedFilePatchesByPath = new Map();
    this.stagedFilePatchesSinceParentCommitByPath = new Map();
    this.unstagedFilePatchesByPath = new Map();
  }

  getCachedFilePatchForPath(filePath, {staged, amending} = {}) {
    if (staged && amending) {
      return this.stagedFilePatchesSinceParentCommitByPath.get(filePath);
    } else if (staged) {
      return this.stagedFilePatchesByPath.get(filePath);
    } else {
      return this.unstagedFilePatchesByPath.get(filePath);
    }
  }

  cacheFilePatchForPath(filePath, filePatch, {staged, amending} = {}) {
    if (staged && amending) {
      this.stagedFilePatchesSinceParentCommitByPath.set(filePath, filePatch);
    } else if (staged) {
      this.stagedFilePatchesByPath.set(filePath, filePatch);
    } else {
      this.unstagedFilePatchesByPath.set(filePath, filePatch);
    }
  }

  async getFilePatchForPath(filePath, options = {}) {
    const cachedFilePatch = this.getCachedFilePatchForPath(filePath, options);
    if (cachedFilePatch) { return cachedFilePatch; }

    if (options.amending) { options.baseCommit = 'HEAD~'; }
    const rawDiff = await this.git.getDiffForFilePath(filePath, options);
    if (rawDiff) {
      const [filePatch] = this.buildFilePatchesFromRawDiffs([rawDiff]);
      this.cacheFilePatchForPath(filePath, filePatch, options);
      return filePatch;
    } else {
      return null;
    }
  }

  buildFilePatchesFromRawDiffs(rawDiffs) {
    const statusMap = {
      '+': 'added',
      '-': 'deleted',
      ' ': 'unchanged',
    };
    return rawDiffs.map(patch => {
      const hunks = patch.hunks.map(hunk => {
        let oldLineNumber = hunk.oldStartLine;
        let newLineNumber = hunk.newStartLine;
        const hunkLines = hunk.lines.map(line => {
          const status = statusMap[line[0]];
          const text = line.slice(1);
          let hunkLine;
          if (status === 'unchanged') {
            hunkLine = new HunkLine(text, status, oldLineNumber, newLineNumber);
            oldLineNumber++;
            newLineNumber++;
          } else if (status === 'added') {
            hunkLine = new HunkLine(text, status, -1, newLineNumber);
            newLineNumber++;
          } else if (status === 'deleted') {
            hunkLine = new HunkLine(text, status, oldLineNumber, -1);
            oldLineNumber++;
          } else if (status === undefined) {
            hunkLine = new HunkLine('\\' + text, status, -1, newLineNumber - 1);
          }
          return hunkLine;
        });
        return new Hunk(hunk.oldStartLine, hunk.newStartLine, hunk.oldLineCount, hunk.newLineCount, hunkLines);
      });
      return new FilePatch(patch.oldPath, patch.newPath, patch.status, hunks);
    });
  }

  async stageFiles(paths) {
    await this.git.stageFiles(paths);
  }

  async unstageFiles(paths) {
    await this.git.unstageFiles(paths);
  }

  async stageFilesFromParentCommit(paths) {
    await this.git.unstageFiles(paths, 'HEAD~');
  }

  async applyPatchToIndex(filePatch) {
    const patchStr = filePatch.getHeaderString() + filePatch.toString();
    await this.git.applyPatchToIndex(patchStr);
  }

  async pathHasMergeMarkers(relativePath) {
    try {
      const contents = await readFile(path.join(this.getWorkingDirectoryPath(), relativePath), 'utf8');
      return MERGE_MARKER_REGEX.test(contents);
    } catch (e) {
      if (e.code === 'ENOENT') { return false; } else { throw e; }
    }
  }

  getMergeHead() {
    return this.git.getMergeHead();
  }

  async getMergeMessage() {
    try {
      const contents = await readFile(path.join(this.getWorkingDirectoryPath(), '.git', 'MERGE_MSG'), 'utf8');
      return contents;
    } catch (e) {
      return null;
    }
  }

  async abortMerge() {
    await this.git.abortMerge();
    return this.refresh();
  }

  isMerging() {
    return this.git.isMerging();
  }

  async commit(message, options) {
    await this.git.commit(this.formatCommitMessage(message), options);
    this.stagedChangesPromise = null;
    this.stagedChangesSinceParentCommitPromise = null;
    this.didUpdate();
  }

  getLastCommit() {
    return this.git.getHeadCommit();
  }

  formatCommitMessage(message) {
    // strip out comments
    const messageWithoutComments = message.replace(/^#.*$/mg, '').trim();

    // hard wrap message (except for first line) at 72 characters
    let results = [];
    messageWithoutComments.split('\n').forEach((line, index) => {
      if (line.length <= 72 || index === 0) {
        results.push(line);
      } else {
        const matches = line.match(/.{1,72}(\s|$)|\S+?(\s|$)/g)
          .map(match => {
            return match.endsWith('\n') ? match.substr(0, match.length - 1) : match;
          });
        results = results.concat(matches);
      }
    });

    return results.join('\n');
  }

  readFileFromIndex(filePath) {
    return this.git.readFileFromIndex(filePath);
  }

  push(branchName, options) {
    return this.git.push(branchName, options);
  }

  fetch(branchName) {
    return this.git.fetch(branchName);
  }

  async pull(branchName) {
    await this.fetch(branchName);
    const remote = await this.git.getRemoteForBranch(branchName);
    return this.git.merge(`${remote}/${branchName}`);
  }

  getAheadCount(branchName) {
    return this.git.getAheadCount(branchName);
  }

  getBehindCount(branchName) {
    return this.git.getBehindCount(branchName);
  }

  getRemoteForBranch(branchName) {
    return this.git.getRemoteForBranch(branchName);
  }

  getCurrentBranch() {
    return this.git.getCurrentBranch();
  }

  getBranches() {
    return this.git.getBranches();
  }

  async checkout(branchName, options) {
    await this.git.checkout(branchName, options);
  }

  setCommitState(state) {
    this.commitState = state;
  }

  getCommitState() {
    return this.commitState;
  }
}
<commit_msg>:fire: unnecessary Repository#didUpdate call<commit_after>/** @babel */
import path from 'path';

import {Emitter} from 'atom';

import GitShellOutStrategy from '../git-shell-out-strategy';
import FilePatch from './file-patch';
import Hunk from './hunk';
import HunkLine from './hunk-line';
import {readFile} from '../helpers';

const MERGE_MARKER_REGEX = /^(>|<){7} \S+$/m;

export class AbortMergeError extends Error {
  constructor(code, filePath) {
    super();
    this.message = `${code}: ${filePath}.`;
    this.code = code;
    this.path = filePath;
    this.stack = new Error().stack;
  }
}

export class CommitError extends Error {
  constructor(code) {
    super();
    this.message = `Commit error: ${code}.`;
    this.code = code;
    this.stack = new Error().stack;
  }
}

export default class Repository {
  static async open(workingDirectory, gitStrategy) {
    const strategy = gitStrategy || new GitShellOutStrategy(workingDirectory.getPath());
    if (await strategy.isGitRepository(workingDirectory)) {
      return new Repository(workingDirectory, strategy);
    } else {
      return null;
    }
  }

  constructor(workingDirectory, gitStrategy) {
    this.workingDirectory = workingDirectory;
    this.emitter = new Emitter();
    this.stagedFilePatchesByPath = new Map();
    this.stagedFilePatchesSinceParentCommitByPath = new Map();
    this.unstagedFilePatchesByPath = new Map();
    this.git = gitStrategy;
    this.destroyed = false;
  }

  destroy() {
    this.destroyed = true;
    this.emitter.emit('did-destroy');
    this.emitter.dispose();
  }

  isDestroyed() {
    return this.destroyed;
  }

  onDidDestroy(callback) {
    return this.emitter.on('did-destroy', callback);
  }

  onDidUpdate(callback) {
    return this.emitter.on('did-update', callback);
  }

  didUpdate() {
    this.emitter.emit('did-update');
  }

  getWorkingDirectory() {
    return this.workingDirectory;
  }

  getWorkingDirectoryPath() {
    return this.getWorkingDirectory().getRealPathSync();
  }

  getGitDirectoryPath() {
    return path.join(this.getWorkingDirectoryPath(), '.git');
  }

  async refresh() {
    /* eslint-disable no-console */
    this.clearCachedFilePatches();
    if (process.env.PRINT_GIT_TIMES) { console.time('refresh'); }
    this.fileStatusesPromise = this.fetchStatusesForChangedFiles();
    this.stagedChangesSinceParentCommitPromise = this.fetchStagedChangesSinceParentCommit();
    await Promise.all([
      this.fileStatusesPromise,
      this.stagedChangesSinceParentCommitPromise,
    ]);
    if (process.env.PRINT_GIT_TIMES) { console.timeEnd('refresh'); }
    this.didUpdate();
    /* eslint-enable no-console */
  }

  fetchStatusesForChangedFiles() {
    return this.git.getStatusesForChangedFiles();
  }

  getStatusesForChangedFiles() {
    if (!this.fileStatusesPromise) {
      this.fileStatusesPromise = this.fetchStatusesForChangedFiles();
    }
    return this.fileStatusesPromise;
  }

  async getUnstagedChanges() {
    const {unstagedFiles} = await this.getStatusesForChangedFiles();
    return Object.keys(unstagedFiles).map(filePath => { return {filePath, status: unstagedFiles[filePath]}; });
  }

  async getStagedChanges() {
    const {stagedFiles} = await this.getStatusesForChangedFiles();
    return Object.keys(stagedFiles).map(filePath => { return {filePath, status: stagedFiles[filePath]}; });
  }

  async getMergeConflicts() {
    const {mergeConflictFiles} = await this.getStatusesForChangedFiles();
    return Object.keys(mergeConflictFiles).map(filePath => {
      return {filePath, status: mergeConflictFiles[filePath]};
    });
  }

  async fetchStagedChangesSinceParentCommit() {
    try {
      const stagedFiles = await this.git.diffFileStatus({staged: true, target: 'HEAD~'});
      return Object.keys(stagedFiles).map(filePath => { return {filePath, status: stagedFiles[filePath]}; });
    } catch (e) {
      if (e.message.includes('ambiguous argument \'HEAD~\'')) {
        return [];
      } else {
        throw e;
      }
    }
  }

  getStagedChangesSinceParentCommit() {
    if (!this.stagedChangesSinceParentCommitPromise) {
      this.stagedChangesSinceParentCommitPromise = this.fetchStagedChangesSinceParentCommit();
    }
    return this.stagedChangesSinceParentCommitPromise;
  }

  clearCachedFilePatches() {
    this.stagedFilePatchesByPath = new Map();
    this.stagedFilePatchesSinceParentCommitByPath = new Map();
    this.unstagedFilePatchesByPath = new Map();
  }

  getCachedFilePatchForPath(filePath, {staged, amending} = {}) {
    if (staged && amending) {
      return this.stagedFilePatchesSinceParentCommitByPath.get(filePath);
    } else if (staged) {
      return this.stagedFilePatchesByPath.get(filePath);
    } else {
      return this.unstagedFilePatchesByPath.get(filePath);
    }
  }

  cacheFilePatchForPath(filePath, filePatch, {staged, amending} = {}) {
    if (staged && amending) {
      this.stagedFilePatchesSinceParentCommitByPath.set(filePath, filePatch);
    } else if (staged) {
      this.stagedFilePatchesByPath.set(filePath, filePatch);
    } else {
      this.unstagedFilePatchesByPath.set(filePath, filePatch);
    }
  }

  async getFilePatchForPath(filePath, options = {}) {
    const cachedFilePatch = this.getCachedFilePatchForPath(filePath, options);
    if (cachedFilePatch) { return cachedFilePatch; }

    if (options.amending) { options.baseCommit = 'HEAD~'; }
    const rawDiff = await this.git.getDiffForFilePath(filePath, options);
    if (rawDiff) {
      const [filePatch] = this.buildFilePatchesFromRawDiffs([rawDiff]);
      this.cacheFilePatchForPath(filePath, filePatch, options);
      return filePatch;
    } else {
      return null;
    }
  }

  buildFilePatchesFromRawDiffs(rawDiffs) {
    const statusMap = {
      '+': 'added',
      '-': 'deleted',
      ' ': 'unchanged',
    };
    return rawDiffs.map(patch => {
      const hunks = patch.hunks.map(hunk => {
        let oldLineNumber = hunk.oldStartLine;
        let newLineNumber = hunk.newStartLine;
        const hunkLines = hunk.lines.map(line => {
          const status = statusMap[line[0]];
          const text = line.slice(1);
          let hunkLine;
          if (status === 'unchanged') {
            hunkLine = new HunkLine(text, status, oldLineNumber, newLineNumber);
            oldLineNumber++;
            newLineNumber++;
          } else if (status === 'added') {
            hunkLine = new HunkLine(text, status, -1, newLineNumber);
            newLineNumber++;
          } else if (status === 'deleted') {
            hunkLine = new HunkLine(text, status, oldLineNumber, -1);
            oldLineNumber++;
          } else if (status === undefined) {
            hunkLine = new HunkLine('\\' + text, status, -1, newLineNumber - 1);
          }
          return hunkLine;
        });
        return new Hunk(hunk.oldStartLine, hunk.newStartLine, hunk.oldLineCount, hunk.newLineCount, hunkLines);
      });
      return new FilePatch(patch.oldPath, patch.newPath, patch.status, hunks);
    });
  }

  async stageFiles(paths) {
    await this.git.stageFiles(paths);
  }

  async unstageFiles(paths) {
    await this.git.unstageFiles(paths);
  }

  async stageFilesFromParentCommit(paths) {
    await this.git.unstageFiles(paths, 'HEAD~');
  }

  async applyPatchToIndex(filePatch) {
    const patchStr = filePatch.getHeaderString() + filePatch.toString();
    await this.git.applyPatchToIndex(patchStr);
  }

  async pathHasMergeMarkers(relativePath) {
    try {
      const contents = await readFile(path.join(this.getWorkingDirectoryPath(), relativePath), 'utf8');
      return MERGE_MARKER_REGEX.test(contents);
    } catch (e) {
      if (e.code === 'ENOENT') { return false; } else { throw e; }
    }
  }

  getMergeHead() {
    return this.git.getMergeHead();
  }

  async getMergeMessage() {
    try {
      const contents = await readFile(path.join(this.getWorkingDirectoryPath(), '.git', 'MERGE_MSG'), 'utf8');
      return contents;
    } catch (e) {
      return null;
    }
  }

  async abortMerge() {
    await this.git.abortMerge();
    return this.refresh();
  }

  isMerging() {
    return this.git.isMerging();
  }

  async commit(message, options) {
    await this.git.commit(this.formatCommitMessage(message), options);
    this.stagedChangesPromise = null;
    this.stagedChangesSinceParentCommitPromise = null;
  }

  getLastCommit() {
    return this.git.getHeadCommit();
  }

  formatCommitMessage(message) {
    // strip out comments
    const messageWithoutComments = message.replace(/^#.*$/mg, '').trim();

    // hard wrap message (except for first line) at 72 characters
    let results = [];
    messageWithoutComments.split('\n').forEach((line, index) => {
      if (line.length <= 72 || index === 0) {
        results.push(line);
      } else {
        const matches = line.match(/.{1,72}(\s|$)|\S+?(\s|$)/g)
          .map(match => {
            return match.endsWith('\n') ? match.substr(0, match.length - 1) : match;
          });
        results = results.concat(matches);
      }
    });

    return results.join('\n');
  }

  readFileFromIndex(filePath) {
    return this.git.readFileFromIndex(filePath);
  }

  push(branchName, options) {
    return this.git.push(branchName, options);
  }

  fetch(branchName) {
    return this.git.fetch(branchName);
  }

  async pull(branchName) {
    await this.fetch(branchName);
    const remote = await this.git.getRemoteForBranch(branchName);
    return this.git.merge(`${remote}/${branchName}`);
  }

  getAheadCount(branchName) {
    return this.git.getAheadCount(branchName);
  }

  getBehindCount(branchName) {
    return this.git.getBehindCount(branchName);
  }

  getRemoteForBranch(branchName) {
    return this.git.getRemoteForBranch(branchName);
  }

  getCurrentBranch() {
    return this.git.getCurrentBranch();
  }

  getBranches() {
    return this.git.getBranches();
  }

  async checkout(branchName, options) {
    await this.git.checkout(branchName, options);
  }

  setCommitState(state) {
    this.commitState = state;
  }

  getCommitState() {
    return this.commitState;
  }
}
<|endoftext|>


<commit_before>var quiddity = require("../quiddity.js"),
    expect = require("expect.js");

describe("quiddity", function() {
    it("should decorate an object and return it", function() {
        var obj = {};
        expect(quiddity(obj)).to.be(obj);
        expect(obj.create).to.be.a("function");
        expect(obj.init).to.be.a("function");
    });

    it("should reject invalid arguments", function() {
        expect(quiddity).withArgs(21120).to.throwError(new Error());
        expect(quiddity).withArgs("foo").to.throwError(new Error());
        expect(quiddity).withArgs([2,3]).to.throwError(new Error());
    });

    describe("create", function() {
        it("should return a derivation of current prototype", function() {
            var obj = quiddity({}),
                derivation = obj.create();
            expect(obj.isPrototypeOf(derivation));
        });

        it("should call prototype init", function() {
            var init = function() {this.foo = 42;},
                obj = quiddity({}, init);

            expect(obj.foo).to.be(undefined);
            expect(obj.create().foo).to.be(42);
        });
    });
});
<commit_msg>test init chaining<commit_after>var quiddity = require("../quiddity.js"),
    expect = require("expect.js");

describe("quiddity", function() {
    it("should decorate an object and return it", function() {
        var obj = {};
        expect(quiddity(obj)).to.be(obj);
        expect(obj.create).to.be.a("function");
        expect(obj.init).to.be.a("function");
    });

    it("should reject invalid arguments", function() {
        expect(quiddity).withArgs(21120).to.throwError(new Error());
        expect(quiddity).withArgs("foo").to.throwError(new Error());
        expect(quiddity).withArgs([2,3]).to.throwError(new Error());
    });

    describe("create", function() {
        it("should return a derivation of current prototype", function() {
            var obj = quiddity({}),
                derivation = obj.create();
            expect(obj.isPrototypeOf(derivation));
        });

        it("should call prototype init", function() {
            var init = function() {this.foo = 42;},
                obj = quiddity({}, init);

            expect(obj.foo).to.be(undefined);
            expect(obj.create().foo).to.be(42);
        });

        it("should be able to chain inits together", function() {
            var fnA = function() {this.value += "A";},
                fnB = function() {this.value += "B";},
                fnC = function() {this.value += "C";},
                obj = quiddity({value:""}, fnA);

            expect(obj.create(fnB).create(fnC).create().value).to.be("ABC");
        });
    });
});
<|endoftext|>


<commit_before>'use strict';
const path = require('path');
const compareVersions = require('compare-versions');
const chalk = require('chalk');

/**
 * @typedef {import('../types').Identifier} Identifier
 * @typedef {import('../types').SimpleSupportStatement} SimpleSupportStatement
 * @typedef {import('../types').SupportBlock} SupportBlock
 * @typedef {import('../types').VersionValue} VersionValue
 */
const browsers = require('..').browsers;

/** @type {Object<string, string[]>} */
const validBrowserVersions = {};
for (const browser of Object.keys(browsers)) {
  validBrowserVersions[browser] = Object.keys(browsers[browser].releases);
}

/**
 * @param {string} browserIdentifier
 * @param {VersionValue} version
 */
function isValidVersion(browserIdentifier, version) {
  if (typeof version === 'string') {
    return validBrowserVersions[browserIdentifier].includes(version);
  } else {
    return true;
  }
}

/**
 * @param {string} dataFilename
 */
function testVersions(dataFilename) {
  const data = require(dataFilename);
  let hasErrors = false;

  /**
   * @param {SupportBlock} supportData
   */
  function checkVersions(supportData) {
    const browsersToCheck = Object.keys(supportData);
    for (const browser of browsersToCheck) {
      if (validBrowserVersions[browser]) {
        /** @type {SimpleSupportStatement[]} */
        const supportStatements = [];
        if (Array.isArray(supportData[browser])) {
          Array.prototype.push.apply(supportStatements, supportData[browser]);
        } else {
          supportStatements.push(supportData[browser]);
        }

        const validBrowserVersionsString =
          'true, false, null' +
          (validBrowserVersions[browser].length > 0
            ? ', ' + validBrowserVersions[browser].join(', ')
            : '');
        const validBrowserVersionsTruthy =
          validBrowserVersions[browser].length > 0
            ? 'true, ' + validBrowserVersions[browser].join(', ')
            : 'true';

        for (const statement of supportStatements) {
          if (!isValidVersion(browser, statement.version_added)) {
            console.error(chalk.red(
              `  version_added: "${
                statement.version_added
              }" is not a valid version number for ${browser}`,
            ));
            console.error(chalk.red(`  Valid ${browser} versions are: ${validBrowserVersionsString}`));
            hasErrors = true;
          }
          if (!isValidVersion(browser, statement.version_removed)) {
            console.error(chalk.red(
              `  version_removed: "${
                statement.version_removed
              }" is not a valid version number for ${browser}`,
            ));
            console.error(chalk.red(`  Valid ${browser} versions are: ${validBrowserVersionsString}`));
            hasErrors = true;
          }
          if ('version_removed' in statement && 'version_added' in statement) {
            if (
              typeof statement.version_added !== 'string' &&
              statement.version_added !== true
            ) {
              console.error(chalk.red(
                `  version_added: "${
                  statement.version_added
                }" is not a valid version number when version_removed is present`,
              ));
              console.error(chalk.red(`  Valid ${browser} versions are: ${validBrowserVersionsTruthy}`));
              hasErrors = true;
            } else if (
              typeof statement.version_added === 'string' &&
              typeof statement.version_removed === 'string' &&
              compareVersions(statement.version_added, statement.version_removed) >= 0
            ) {
              console.error(chalk.red(
                `  version_removed: "${
                  statement.version_removed
                }" must be greater than version_added: "${
                  statement.version_added
                }"`,
              ));
              hasErrors = true;
            }
          }
        }
      }
    }
  }

  /**
   * @param {Identifier} data
   */
  function findSupport(data) {
    for (const prop in data) {
      if (prop === '__compat' && data[prop].support) {
        checkVersions(data[prop].support);
      }
      const sub = data[prop];
      if (typeof sub === 'object') {
        findSupport(sub);
      }
    }
  }
  findSupport(data);

  if (hasErrors) {
    console.error(chalk.red(`  File : ${path.relative(process.cwd(), dataFilename)}`));
    console.error(chalk.red('  Browser version error(s)'));
    return true;
  } else {
    return false;
  }
}

module.exports = testVersions;
<commit_msg>Linter: remove redundant array length test and switch to string literals (#4082)<commit_after>'use strict';
const path = require('path');
const compareVersions = require('compare-versions');
const chalk = require('chalk');

/**
 * @typedef {import('../types').Identifier} Identifier
 * @typedef {import('../types').SimpleSupportStatement} SimpleSupportStatement
 * @typedef {import('../types').SupportBlock} SupportBlock
 * @typedef {import('../types').VersionValue} VersionValue
 */
const browsers = require('..').browsers;

/** @type {Object<string, string[]>} */
const validBrowserVersions = {};
for (const browser of Object.keys(browsers)) {
  validBrowserVersions[browser] = Object.keys(browsers[browser].releases);
}

/**
 * @param {string} browserIdentifier
 * @param {VersionValue} version
 */
function isValidVersion(browserIdentifier, version) {
  if (typeof version === 'string') {
    return validBrowserVersions[browserIdentifier].includes(version);
  } else {
    return true;
  }
}

/**
 * @param {string} dataFilename
 */
function testVersions(dataFilename) {
  const data = require(dataFilename);
  let hasErrors = false;

  /**
   * @param {SupportBlock} supportData
   */
  function checkVersions(supportData) {
    const browsersToCheck = Object.keys(supportData);
    for (const browser of browsersToCheck) {
      if (validBrowserVersions[browser]) {
        /** @type {SimpleSupportStatement[]} */
        const supportStatements = [];
        if (Array.isArray(supportData[browser])) {
          Array.prototype.push.apply(supportStatements, supportData[browser]);
        } else {
          supportStatements.push(supportData[browser]);
        }

        const validBrowserVersionsString = `true, false, null, ${validBrowserVersions[browser].join(', ')}`;
        const validBrowserVersionsTruthy = `true, ${validBrowserVersions[browser].join(', ')}`;

        for (const statement of supportStatements) {
          if (!isValidVersion(browser, statement.version_added)) {
            console.error(chalk.red(
              `  version_added: "${
                statement.version_added
              }" is not a valid version number for ${browser}`,
            ));
            console.error(chalk.red(`  Valid ${browser} versions are: ${validBrowserVersionsString}`));
            hasErrors = true;
          }
          if (!isValidVersion(browser, statement.version_removed)) {
            console.error(chalk.red(
              `  version_removed: "${
                statement.version_removed
              }" is not a valid version number for ${browser}`,
            ));
            console.error(chalk.red(`  Valid ${browser} versions are: ${validBrowserVersionsString}`));
            hasErrors = true;
          }
          if ('version_removed' in statement && 'version_added' in statement) {
            if (
              typeof statement.version_added !== 'string' &&
              statement.version_added !== true
            ) {
              console.error(chalk.red(
                `  version_added: "${
                  statement.version_added
                }" is not a valid version number when version_removed is present`,
              ));
              console.error(chalk.red(`  Valid ${browser} versions are: ${validBrowserVersionsTruthy}`));
              hasErrors = true;
            } else if (
              typeof statement.version_added === 'string' &&
              typeof statement.version_removed === 'string' &&
              compareVersions(statement.version_added, statement.version_removed) >= 0
            ) {
              console.error(chalk.red(
                `  version_removed: "${
                  statement.version_removed
                }" must be greater than version_added: "${
                  statement.version_added
                }"`,
              ));
              hasErrors = true;
            }
          }
        }
      }
    }
  }

  /**
   * @param {Identifier} data
   */
  function findSupport(data) {
    for (const prop in data) {
      if (prop === '__compat' && data[prop].support) {
        checkVersions(data[prop].support);
      }
      const sub = data[prop];
      if (typeof sub === 'object') {
        findSupport(sub);
      }
    }
  }
  findSupport(data);

  if (hasErrors) {
    console.error(chalk.red(`  File : ${path.relative(process.cwd(), dataFilename)}`));
    console.error(chalk.red('  Browser version error(s)'));
    return true;
  } else {
    return false;
  }
}

module.exports = testVersions;
<|endoftext|>


<commit_before>const fs = require('../../core/fs.js');
const utils = require('../../utils/utils.js');
const namehash = require('eth-ens-namehash');
const async = require('async');
const embarkJsUtils = require('embarkjs').Utils;
const reverseAddrSuffix = '.addr.reverse';

class ENS {
  constructor(embark, _options) {
    this.env = embark.env;
    this.isDev = embark.config.blockchainConfig.isDev;
    this.logger = embark.logger;
    this.events = embark.events;
    this.namesConfig = embark.config.namesystemConfig;
    this.registration = this.namesConfig.register || {};
    this.embark = embark;

    if (this.namesConfig === {} ||
      this.namesConfig.enabled !== true ||
      this.namesConfig.available_providers.indexOf('ens') < 0) {
      return;
    }
    this.doSetENSProvider = this.namesConfig.provider === 'ens';

    this.addENSToEmbarkJS();
    this.configureContracts();
    this.registerEvents();
  }

  registerEvents() {
    this.events.once("contracts:deploy:afterAll", this.setProviderAndRegisterDomains.bind(this));

    this.events.setCommandHandler("storage:ens:associate", this.associateStorageToEns.bind(this));
  }

  setProviderAndRegisterDomains(cb) {
    const self = this;
    async.parallel([
      function getENSRegistry(paraCb) {
        self.events.request('contracts:contract', "ENSRegistry", (contract) => {
          paraCb(null, contract);
        });
      },
      function getRegistrar(paraCb) {
        self.events.request('contracts:contract', "FIFSRegistrar", (contract) => {
          paraCb(null, contract);
        });
      },
      function getResolver(paraCb) {
        self.events.request('contracts:contract', "Resolver", (contract) => {
          paraCb(null, contract);
        });
      }
    ], (err, results) => {
      if (err) {
        return cb(err);
      }
      if (!results[0] || !results[1] || !results[2]) {
        return cb(__('Aborting ENS setup as some of the contracts did not deploy properly'));
      }
      // result[0] => ENSRegistry; result[1] => FIFSRegistrar; result[2] => FIFSRegistrar
      let config = {
        env: self.env,
        registration: self.registration,
        registryAbi: results[0].abiDefinition,
        registryAddress: results[0].deployedAddress,
        registrarAbi: results[1].abiDefinition,
        registrarAddress: results[1].deployedAddress,
        resolverAbi: results[2].abiDefinition,
        resolverAddress: results[2].deployedAddress
      };

      if (self.doSetENSProvider) {
        self.addSetProvider(config);
      }

      if ((!self.isDev && self.env !== 'privatenet') || !self.registration || !self.registration.subdomains || !Object.keys(self.registration.subdomains).length) {
        return cb();
      }
      self.registerConfigDomains(config, cb);
    });
  }

  associateStorageToEns(options, cb) {
    const self = this;
    // Code inspired by https://github.com/monkybrain/ipfs-to-ens
    const {name, storageHash} = options;

    if (!utils.isValidEthDomain(name)) {
      return cb('Invalid domain name ' + name);
    }

    let hashedName = namehash.hash(name);
    let contentHash;
    try {
      contentHash = utils.hashTo32ByteHexString(storageHash);
    } catch (e) {
      return cb('Invalid IPFS hash');
    }

    // Set content
    async.waterfall([
      function getRegistryABI(next) {
        self.events.request('contracts:contract', "ENSRegistry", (contract) => {
          next(null, contract);
        });
      },
      function createRegistryContract(contract, next) {
        self.events.request("blockchain:contract:create",
          {abi: contract.abiDefinition, address: contract.deployedAddress},
          (resolver) => {
            next(null, resolver);
          });
      },
      function getResolverForName(registry, next) {
        registry.methods.resolver(hashedName).call((err, resolverAddress) => {
          if (err) {
            return cb(err);
          }
          if (resolverAddress === '0x0000000000000000000000000000000000000000') {
            return cb('Name not yet registered');
          }
          next(null, resolverAddress);
        });
      },
      function getResolverABI(resolverAddress, next) {
        self.events.request('contracts:contract', "Resolver", (contract) => {
          next(null, resolverAddress, contract);
        });
      },
      function createResolverContract(resolverAddress, contract, next) {
        self.events.request("blockchain:contract:create",
          {abi: contract.abiDefinition, address: resolverAddress},
          (resolver) => {
            next(null, resolver);
          });
      },
      function getDefaultAccount(resolver, next) {
        self.events.request("blockchain:defaultAccount:get", (defaultAccount) => {
          next(null, resolver, defaultAccount);
        });
      },
      function setContent(resolver, defaultAccount, next) {
        resolver.methods.setContent(hashedName, contentHash).send({from: defaultAccount}).then((transaction) => {
          if (transaction.status !== "0x1" && transaction.status !== "0x01" && transaction.status !== true) {
            return next('Association failed. Status: ' + transaction.status);
          }
          next();
        }).catch(next);
      }
    ], cb);
  }

  registerConfigDomains(config, cb) {
    const self = this;
    const register = require('./register');
    const secureSend = embarkJsUtils.secureSend;
    self.events.request("blockchain:defaultAccount:get", (defaultAccount) => {
      async.parallel([
        function createRegistryContract(paraCb) {
          self.events.request("blockchain:contract:create",
            {abi: config.registryAbi, address: config.registryAddress},
            (registry) => {
              paraCb(null, registry);
            });
        },
        function createRegistrarContract(paraCb) {
          self.events.request("blockchain:contract:create",
            {abi: config.registrarAbi, address: config.registrarAddress},
            (registrar) => {
              paraCb(null, registrar);
            });
        },
        function createResolverContract(paraCb) {
          self.events.request("blockchain:contract:create",
            {abi: config.resolverAbi, address: config.resolverAddress},
            (resolver) => {
              paraCb(null, resolver);
            });
        }
      ], function(err, contracts) {
        if (err) {
          return cb(err);
        }
        const [ens, registrar, resolver] = contracts;

        async.each(Object.keys(self.registration.subdomains), (subDomainName, eachCb) => {
          const address = self.registration.subdomains[subDomainName];
          const reverseNode = utils.soliditySha3(address.toLowerCase().substr(2) + reverseAddrSuffix);
          register(ens, registrar, resolver, defaultAccount, subDomainName, self.registration.rootDomain,
            reverseNode, address, self.logger, secureSend, eachCb);
        }, cb);

      });
    });
  }

  addENSToEmbarkJS() {
    const self = this;

    // get namehash, import it into file
    self.events.request("version:get:eth-ens-namehash", function(EnsNamehashVersion) {
      let currentEnsNamehashVersion = require('../../../package.json').dependencies["eth-ens-namehash"];
      if (EnsNamehashVersion !== currentEnsNamehashVersion) {
        self.events.request("version:getPackageLocation", "eth-ens-namehash", EnsNamehashVersion, function(err, location) {
          self.embark.registerImportFile("eth-ens-namehash", fs.dappPath(location));
        });
      }
    });

    let code = fs.readFileSync(utils.joinPath(__dirname, 'register.js')).toString();
    code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs.js')).toString();
    code += "\nEmbarkJS.Names.registerProvider('ens', __embarkENS);";

    this.embark.addCodeToEmbarkJS(code);
  }

  configureContracts() {
    const config = {
      "default": {
        "gas": "auto",
        "contracts": {
          "ENS": {
            "deploy": false,
            "silent": true
          },
          "ENSRegistry": {
            "deploy": true,
            "silent": true,
            "args": []
          },
          "Resolver": {
            "deploy": true,
            "silent": true,
            "args": ["$ENSRegistry"]
          },
          "FIFSRegistrar": {
            "deploy": false
          }
        }
      },
      "ropsten": {
        "contracts": {
          "ENSRegistry": {
            "address": "0x112234455c3a32fd11230c42e7bccd4a84e02010",
            "silent": true
          },
          "Resolver": {
            "deploy": false
          },
          "FIFSRegistrar": {
            "deploy": false
          }
        }
      },
      "rinkeby": {
        "contracts": {
          "ENSRegistry": {
            "address": "0xe7410170f87102DF0055eB195163A03B7F2Bff4A",
            "silent": true
          },
          "Resolver": {
            "deploy": false
          },
          "FIFSRegistrar": {
            "deploy": false
          }
        }
      },
      "livenet": {
        "contracts": {
          "ENSRegistry": {
            "address": "0x314159265dd8dbb310642f98f50c066173c1259b",
            "silent": true
          },
          "Resolver": {
            "deploy": false
          },
          "FIFSRegistrar": {
            "deploy": false
          }
        }
      }
    };
    config.testnet = config.ropsten;

    if (this.registration && this.registration.rootDomain) {
      // Register root domain if it is defined
      const rootNode = namehash.hash(this.registration.rootDomain);
      config.default.contracts['FIFSRegistrar'] = {
        "deploy": true,
        "silent": true,
        "args": ["$ENSRegistry", rootNode],
        "onDeploy": [
          `ENSRegistry.methods.setOwner('${rootNode}', web3.eth.defaultAccount).send({from: web3.eth.defaultAccount}).then(() => {
            ENSRegistry.methods.setResolver('${rootNode}', "$Resolver").send({from: web3.eth.defaultAccount});
            var reverseNode = web3.utils.soliditySha3(web3.eth.defaultAccount.toLowerCase().substr(2) + '${reverseAddrSuffix}');
            ENSRegistry.methods.setResolver(reverseNode, "$Resolver").send({from: web3.eth.defaultAccount});
            Resolver.methods.setAddr('${rootNode}', web3.eth.defaultAccount).send({from: web3.eth.defaultAccount});
            Resolver.methods.setName(reverseNode, '${this.registration.rootDomain}').send({from: web3.eth.defaultAccount});
          })`
        ]
      };
    }
    config.privatenet = config.development;
    this.embark.registerContractConfiguration(config);

    if (this.isDev || this.env === 'privatenet') {
      this.embark.events.request("config:contractsFiles:add", this.embark.pathToFile('./contracts/ENSRegistry.sol'));
      this.embark.events.request("config:contractsFiles:add", this.embark.pathToFile('./contracts/FIFSRegistrar.sol'));
      this.embark.events.request("config:contractsFiles:add", this.embark.pathToFile('./contracts/Resolver.sol'));
    }
  }

  addSetProvider(config) {
    let code = "\nEmbarkJS.Names.setProvider('ens'," + JSON.stringify(config) + ");";

    let shouldInit = (namesConfig) => {
      return (namesConfig.provider === 'ens' && namesConfig.enabled === true);
    };

    this.embark.addProviderInit('names', code, shouldInit);
    this.embark.addConsoleProviderInit('names', code, shouldInit);
  }
}

module.exports = ENS;
<commit_msg>Prevent embark to crash when there is no config for ens<commit_after>const fs = require('../../core/fs.js');
const utils = require('../../utils/utils.js');
const namehash = require('eth-ens-namehash');
const async = require('async');
const embarkJsUtils = require('embarkjs').Utils;
const reverseAddrSuffix = '.addr.reverse';

class ENS {
  constructor(embark, _options) {
    this.env = embark.env;
    this.isDev = embark.config.blockchainConfig.isDev;
    this.logger = embark.logger;
    this.events = embark.events;
    this.namesConfig = embark.config.namesystemConfig;
    this.registration = this.namesConfig.register || {};
    this.embark = embark;

    if (this.namesConfig === {} ||
      this.namesConfig.enabled !== true ||
      this.namesConfig.available_providers.indexOf('ens') < 0) {
      return;
    }
    this.doSetENSProvider = this.namesConfig.provider === 'ens';

    this.addENSToEmbarkJS();
    this.configureContracts();
    this.registerEvents();
  }

  registerEvents() {
    this.events.once("contracts:deploy:afterAll", this.setProviderAndRegisterDomains.bind(this));

    this.events.setCommandHandler("storage:ens:associate", this.associateStorageToEns.bind(this));
  }

  setProviderAndRegisterDomains(cb = () => {}) {
    const self = this;
    async.parallel([
      function getENSRegistry(paraCb) {
        self.events.request('contracts:contract', "ENSRegistry", (contract) => {
          paraCb(null, contract);
        });
      },
      function getRegistrar(paraCb) {
        self.events.request('contracts:contract', "FIFSRegistrar", (contract) => {
          paraCb(null, contract);
        });
      },
      function getResolver(paraCb) {
        self.events.request('contracts:contract', "Resolver", (contract) => {
          paraCb(null, contract);
        });
      }
    ], (err, results) => {
      if (err) {
        return cb(err);
      }
      if (!results[0] || !results[1] || !results[2]) {
        return cb(__('Aborting ENS setup as some of the contracts did not deploy properly'));
      }
      // result[0] => ENSRegistry; result[1] => FIFSRegistrar; result[2] => FIFSRegistrar
      let config = {
        env: self.env,
        registration: self.registration,
        registryAbi: results[0].abiDefinition,
        registryAddress: results[0].deployedAddress,
        registrarAbi: results[1].abiDefinition,
        registrarAddress: results[1].deployedAddress,
        resolverAbi: results[2].abiDefinition,
        resolverAddress: results[2].deployedAddress
      };

      if (self.doSetENSProvider) {
        self.addSetProvider(config);
      }

      if ((!self.isDev && self.env !== 'privatenet') || !self.registration || !self.registration.subdomains || !Object.keys(self.registration.subdomains).length) {
        return cb();
      }
      self.registerConfigDomains(config, cb);
    });
  }

  associateStorageToEns(options, cb) {
    const self = this;
    // Code inspired by https://github.com/monkybrain/ipfs-to-ens
    const {name, storageHash} = options;

    if (!utils.isValidEthDomain(name)) {
      return cb('Invalid domain name ' + name);
    }

    let hashedName = namehash.hash(name);
    let contentHash;
    try {
      contentHash = utils.hashTo32ByteHexString(storageHash);
    } catch (e) {
      return cb('Invalid IPFS hash');
    }

    // Set content
    async.waterfall([
      function getRegistryABI(next) {
        self.events.request('contracts:contract', "ENSRegistry", (contract) => {
          next(null, contract);
        });
      },
      function createRegistryContract(contract, next) {
        self.events.request("blockchain:contract:create",
          {abi: contract.abiDefinition, address: contract.deployedAddress},
          (resolver) => {
            next(null, resolver);
          });
      },
      function getResolverForName(registry, next) {
        registry.methods.resolver(hashedName).call((err, resolverAddress) => {
          if (err) {
            return cb(err);
          }
          if (resolverAddress === '0x0000000000000000000000000000000000000000') {
            return cb('Name not yet registered');
          }
          next(null, resolverAddress);
        });
      },
      function getResolverABI(resolverAddress, next) {
        self.events.request('contracts:contract', "Resolver", (contract) => {
          next(null, resolverAddress, contract);
        });
      },
      function createResolverContract(resolverAddress, contract, next) {
        self.events.request("blockchain:contract:create",
          {abi: contract.abiDefinition, address: resolverAddress},
          (resolver) => {
            next(null, resolver);
          });
      },
      function getDefaultAccount(resolver, next) {
        self.events.request("blockchain:defaultAccount:get", (defaultAccount) => {
          next(null, resolver, defaultAccount);
        });
      },
      function setContent(resolver, defaultAccount, next) {
        resolver.methods.setContent(hashedName, contentHash).send({from: defaultAccount}).then((transaction) => {
          if (transaction.status !== "0x1" && transaction.status !== "0x01" && transaction.status !== true) {
            return next('Association failed. Status: ' + transaction.status);
          }
          next();
        }).catch(next);
      }
    ], cb);
  }

  registerConfigDomains(config, cb) {
    const self = this;
    const register = require('./register');
    const secureSend = embarkJsUtils.secureSend;
    self.events.request("blockchain:defaultAccount:get", (defaultAccount) => {
      async.parallel([
        function createRegistryContract(paraCb) {
          self.events.request("blockchain:contract:create",
            {abi: config.registryAbi, address: config.registryAddress},
            (registry) => {
              paraCb(null, registry);
            });
        },
        function createRegistrarContract(paraCb) {
          self.events.request("blockchain:contract:create",
            {abi: config.registrarAbi, address: config.registrarAddress},
            (registrar) => {
              paraCb(null, registrar);
            });
        },
        function createResolverContract(paraCb) {
          self.events.request("blockchain:contract:create",
            {abi: config.resolverAbi, address: config.resolverAddress},
            (resolver) => {
              paraCb(null, resolver);
            });
        }
      ], function(err, contracts) {
        if (err) {
          return cb(err);
        }
        const [ens, registrar, resolver] = contracts;

        async.each(Object.keys(self.registration.subdomains), (subDomainName, eachCb) => {
          const address = self.registration.subdomains[subDomainName];
          const reverseNode = utils.soliditySha3(address.toLowerCase().substr(2) + reverseAddrSuffix);
          register(ens, registrar, resolver, defaultAccount, subDomainName, self.registration.rootDomain,
            reverseNode, address, self.logger, secureSend, eachCb);
        }, cb);

      });
    });
  }

  addENSToEmbarkJS() {
    const self = this;

    // get namehash, import it into file
    self.events.request("version:get:eth-ens-namehash", function(EnsNamehashVersion) {
      let currentEnsNamehashVersion = require('../../../package.json').dependencies["eth-ens-namehash"];
      if (EnsNamehashVersion !== currentEnsNamehashVersion) {
        self.events.request("version:getPackageLocation", "eth-ens-namehash", EnsNamehashVersion, function(err, location) {
          self.embark.registerImportFile("eth-ens-namehash", fs.dappPath(location));
        });
      }
    });

    let code = fs.readFileSync(utils.joinPath(__dirname, 'register.js')).toString();
    code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs.js')).toString();
    code += "\nEmbarkJS.Names.registerProvider('ens', __embarkENS);";

    this.embark.addCodeToEmbarkJS(code);
  }

  configureContracts() {
    const config = {
      "default": {
        "gas": "auto",
        "contracts": {
          "ENS": {
            "deploy": false,
            "silent": true
          },
          "ENSRegistry": {
            "deploy": true,
            "silent": true,
            "args": []
          },
          "Resolver": {
            "deploy": true,
            "silent": true,
            "args": ["$ENSRegistry"]
          },
          "FIFSRegistrar": {
            "deploy": false
          }
        }
      },
      "ropsten": {
        "contracts": {
          "ENSRegistry": {
            "address": "0x112234455c3a32fd11230c42e7bccd4a84e02010",
            "silent": true
          },
          "Resolver": {
            "deploy": false
          },
          "FIFSRegistrar": {
            "deploy": false
          }
        }
      },
      "rinkeby": {
        "contracts": {
          "ENSRegistry": {
            "address": "0xe7410170f87102DF0055eB195163A03B7F2Bff4A",
            "silent": true
          },
          "Resolver": {
            "deploy": false
          },
          "FIFSRegistrar": {
            "deploy": false
          }
        }
      },
      "livenet": {
        "contracts": {
          "ENSRegistry": {
            "address": "0x314159265dd8dbb310642f98f50c066173c1259b",
            "silent": true
          },
          "Resolver": {
            "deploy": false
          },
          "FIFSRegistrar": {
            "deploy": false
          }
        }
      }
    };
    config.testnet = config.ropsten;

    if (this.registration && this.registration.rootDomain) {
      // Register root domain if it is defined
      const rootNode = namehash.hash(this.registration.rootDomain);
      config.default.contracts['FIFSRegistrar'] = {
        "deploy": true,
        "silent": true,
        "args": ["$ENSRegistry", rootNode],
        "onDeploy": [
          `ENSRegistry.methods.setOwner('${rootNode}', web3.eth.defaultAccount).send({from: web3.eth.defaultAccount}).then(() => {
            ENSRegistry.methods.setResolver('${rootNode}', "$Resolver").send({from: web3.eth.defaultAccount});
            var reverseNode = web3.utils.soliditySha3(web3.eth.defaultAccount.toLowerCase().substr(2) + '${reverseAddrSuffix}');
            ENSRegistry.methods.setResolver(reverseNode, "$Resolver").send({from: web3.eth.defaultAccount});
            Resolver.methods.setAddr('${rootNode}', web3.eth.defaultAccount).send({from: web3.eth.defaultAccount});
            Resolver.methods.setName(reverseNode, '${this.registration.rootDomain}').send({from: web3.eth.defaultAccount});
          })`
        ]
      };
    }
    config.privatenet = config.development;
    this.embark.registerContractConfiguration(config);

    if (this.isDev || this.env === 'privatenet') {
      this.embark.events.request("config:contractsFiles:add", this.embark.pathToFile('./contracts/ENSRegistry.sol'));
      this.embark.events.request("config:contractsFiles:add", this.embark.pathToFile('./contracts/FIFSRegistrar.sol'));
      this.embark.events.request("config:contractsFiles:add", this.embark.pathToFile('./contracts/Resolver.sol'));
    }
  }

  addSetProvider(config) {
    let code = "\nEmbarkJS.Names.setProvider('ens'," + JSON.stringify(config) + ");";

    let shouldInit = (namesConfig) => {
      return (namesConfig.provider === 'ens' && namesConfig.enabled === true);
    };

    this.embark.addProviderInit('names', code, shouldInit);
    this.embark.addConsoleProviderInit('names', code, shouldInit);
  }
}

module.exports = ENS;
<|endoftext|>


<commit_before>/*
 * file.js: Simple file storage engine for nconf files
 *
 * (C) 2011, Charlie Robbins
 *
 */

var fs = require('fs'),
    path = require('path'),
    util = require('util'),
    formats = require('../formats'),
    Memory = require('./memory').Memory;

//
// ### function File (options)
// #### @options {Object} Options for this instance
// Constructor function for the File nconf store, a simple abstraction
// around the Memory store that can persist configuration to disk.
//
var File = exports.File = function (options) {
  if (!options || !options.file) {
    throw new Error ('Missing required option `files`');
  }

  Memory.call(this, options);

  this.type   = 'file';
  this.file   = options.file;
  this.dir    = options.dir    || process.cwd();
  this.format = options.format || formats.json;

  if (options.search) {
    this.search(this.dir);
  }
};

// Inherit from the Memory store
util.inherits(File, Memory);

//
// ### function save (value, callback)
// #### @value {Object} _Ignored_ Left here for consistency
// #### @callback {function} Continuation to respond to when complete.
// Saves the current configuration object to disk at `this.file`
// using the format specified by `this.format`.
//
File.prototype.save = function (value, callback) {
  if (!callback) {
    callback = value;
    value = null;
  }

  fs.writeFile(this.file, this.format.stringify(this.store), function (err) {
    return err ? callback(err) : callback();
  });
};

//
// ### function saveSync (value, callback)
// #### @value {Object} _Ignored_ Left here for consistency
// #### @callback {function} **Optional** Continuation to respond to when complete.
// Saves the current configuration object to disk at `this.file`
// using the format specified by `this.format` synchronously.
//
File.prototype.saveSync = function (value) {
  try {
    fs.writeFileSync(this.file, this.format.stringify(this.store));
  }
  catch (ex) {
    throw(ex);
  }
};

//
// ### function load (callback)
// #### @callback {function} Continuation to respond to when complete.
// Responds with an Object representing all keys associated in this instance.
//
File.prototype.load = function (callback) {
  var self = this;

  path.exists(self.file, function (exists) {
    if (!exists) {
      //
      // If the path we are attempting to load doesn't exist, create it
      //
      self.save({}, function (err) {
        self.store = {};
        return callback(err, self.store);
      });
    }
    else {
      //
      // Else, the path exists, read it from disk
      //
      fs.readFile(self.file, function (err, data) {
        if (err) {
          return callback(err);
        }

        try {
          self.store = self.format.parse(data.toString());
        }
        catch (ex) {
          return callback(new Error("Error parsing your JSON configuration file."));
        }

        callback(null, self.store);
      });
    }
  });
};

//
// ### function load (callback)
// #### @callback {function} **Optional** Continuation to respond to when complete.
// Attempts to load the data stored in `this.file` synchronously and responds appropriately.
//
File.prototype.loadSync = function () {
  var data, self = this;

  if (!path.existsSync(self.file)) {
    //
    // If the path we are attempting to load doesn't exist, create it
    //
    self.saveSync({});
    self.store = {};
    data = {};
  }
  else {
    //
    // Else, the path exists, read it from disk
    //
    try {
      data = this.format.parse(fs.readFileSync(this.file, 'utf8'));
      this.store = data;
    }
    catch (ex) {
      throw new Error("Error parsing your JSON configuration file.")
    }
  }

  return data;
};

//
// ### function search (base)
// #### @base {string} Base directory (or file) to begin searching for the target file.
// Attempts to find `this.file` by iteratively searching up the
// directory structure
//
File.prototype.search = function (base) {
  var looking = true,
      fullpath,
      previous,
      stats;

  base = base || process.cwd();

  if (this.file[0] === '/') {
    //
    // If filename for this instance is a fully qualified path
    // (i.e. it starts with a `'/'`) then check if it exists
    //
    try {
      stats = fs.statSync(fs.realpathSync(this.file));
      if (stats.isFile()) {
        fullpath = this.file;
        looking = false;
      }
    }
    catch (ex) {
      //
      // Ignore errors
      //
    }
  }

  if (looking && base) {
    //
    // Attempt to stat the realpath located at `base`
    // if the directory does not exist then return false.
    //
    try {
      var stat = fs.statSync(fs.realpathSync(base));
      looking = stat.isDirectory();
    }
    catch (ex) {
      return false;
    }
  }

  while (looking) {
    //
    // Iteratively look up the directory structure from `base`
    //
    try {
      stats = fs.statSync(fs.realpathSync(fullpath = path.join(base, this.file)));
      looking = stats.isDirectory();
    }
    catch (ex) {
      previous = base;
      base = path.dirname(base);

      if (previous === base) {
        //
        // If we've reached the top of the directory structure then simply use
        // the default file path.
        //
        try {
          stats = fs.statSync(fs.realpathSync(fullpath = path.join(this.dir, this.file)));
          if (stats.isDirectory()) {
            fullpath = undefined;
          }
        }
        catch (ex) {
          //
          // Ignore errors
          //
        }

        looking = false;
      }
    }
  }

  //
  // Set the file for this instance to the fullpath
  // that we have found during the search. In the event that
  // the search was unsuccessful use the original value for `this.file`.
  //
  this.file = fullpath || this.file;

  return fullpath;
};<commit_msg>[api fix] Dont eagerly create config files in `.load()` and `.loadSync()`<commit_after>/*
 * file.js: Simple file storage engine for nconf files
 *
 * (C) 2011, Charlie Robbins
 *
 */

var fs = require('fs'),
    path = require('path'),
    util = require('util'),
    formats = require('../formats'),
    Memory = require('./memory').Memory;

//
// ### function File (options)
// #### @options {Object} Options for this instance
// Constructor function for the File nconf store, a simple abstraction
// around the Memory store that can persist configuration to disk.
//
var File = exports.File = function (options) {
  if (!options || !options.file) {
    throw new Error ('Missing required option `files`');
  }

  Memory.call(this, options);

  this.type   = 'file';
  this.file   = options.file;
  this.dir    = options.dir    || process.cwd();
  this.format = options.format || formats.json;

  if (options.search) {
    this.search(this.dir);
  }
};

// Inherit from the Memory store
util.inherits(File, Memory);

//
// ### function save (value, callback)
// #### @value {Object} _Ignored_ Left here for consistency
// #### @callback {function} Continuation to respond to when complete.
// Saves the current configuration object to disk at `this.file`
// using the format specified by `this.format`.
//
File.prototype.save = function (value, callback) {
  if (!callback) {
    callback = value;
    value = null;
  }

  fs.writeFile(this.file, this.format.stringify(this.store), function (err) {
    return err ? callback(err) : callback();
  });
};

//
// ### function saveSync (value, callback)
// #### @value {Object} _Ignored_ Left here for consistency
// #### @callback {function} **Optional** Continuation to respond to when complete.
// Saves the current configuration object to disk at `this.file`
// using the format specified by `this.format` synchronously.
//
File.prototype.saveSync = function (value) {
  try {
    fs.writeFileSync(this.file, this.format.stringify(this.store));
  }
  catch (ex) {
    throw(ex);
  }
};

//
// ### function load (callback)
// #### @callback {function} Continuation to respond to when complete.
// Responds with an Object representing all keys associated in this instance.
//
File.prototype.load = function (callback) {
  var self = this;

  path.exists(self.file, function (exists) {
    if (!exists) {
      return callback(null, {});
    }

    //
    // Else, the path exists, read it from disk
    //
    fs.readFile(self.file, function (err, data) {
      if (err) {
        return callback(err);
      }

      try {
        self.store = self.format.parse(data.toString());
      }
      catch (ex) {
        return callback(new Error("Error parsing your JSON configuration file."));
      }

      callback(null, self.store);
    });
  });
};

//
// ### function loadSync (callback)
// Attempts to load the data stored in `this.file` synchronously
// and responds appropriately.
//
File.prototype.loadSync = function () {
  var data, self = this;

  if (!path.existsSync(self.file)) {
    self.store = {};
    data = {};
  }
  else {
    //
    // Else, the path exists, read it from disk
    //
    try {
      data = this.format.parse(fs.readFileSync(this.file, 'utf8'));
      this.store = data;
    }
    catch (ex) {
      throw new Error("Error parsing your JSON configuration file.")
    }
  }

  return data;
};

//
// ### function search (base)
// #### @base {string} Base directory (or file) to begin searching for the target file.
// Attempts to find `this.file` by iteratively searching up the
// directory structure
//
File.prototype.search = function (base) {
  var looking = true,
      fullpath,
      previous,
      stats;

  base = base || process.cwd();

  if (this.file[0] === '/') {
    //
    // If filename for this instance is a fully qualified path
    // (i.e. it starts with a `'/'`) then check if it exists
    //
    try {
      stats = fs.statSync(fs.realpathSync(this.file));
      if (stats.isFile()) {
        fullpath = this.file;
        looking = false;
      }
    }
    catch (ex) {
      //
      // Ignore errors
      //
    }
  }

  if (looking && base) {
    //
    // Attempt to stat the realpath located at `base`
    // if the directory does not exist then return false.
    //
    try {
      var stat = fs.statSync(fs.realpathSync(base));
      looking = stat.isDirectory();
    }
    catch (ex) {
      return false;
    }
  }

  while (looking) {
    //
    // Iteratively look up the directory structure from `base`
    //
    try {
      stats = fs.statSync(fs.realpathSync(fullpath = path.join(base, this.file)));
      looking = stats.isDirectory();
    }
    catch (ex) {
      previous = base;
      base = path.dirname(base);

      if (previous === base) {
        //
        // If we've reached the top of the directory structure then simply use
        // the default file path.
        //
        try {
          stats = fs.statSync(fs.realpathSync(fullpath = path.join(this.dir, this.file)));
          if (stats.isDirectory()) {
            fullpath = undefined;
          }
        }
        catch (ex) {
          //
          // Ignore errors
          //
        }

        looking = false;
      }
    }
  }

  //
  // Set the file for this instance to the fullpath
  // that we have found during the search. In the event that
  // the search was unsuccessful use the original value for `this.file`.
  //
  this.file = fullpath || this.file;

  return fullpath;
};<|endoftext|>


<commit_before>"use strict";

const Process = require("ez-spawn/lib/process");

module.exports = normalizeResults;

/**
 * Converts an EZ-Spawn Process or ProcessError into a normalized POJO.
 *
 * @param {object} process - An EZ-Spawn Process or ProcessError
 * @returns {object} - The normalized POJO
 */
function normalizeResults (process) {
  // Copy all properties of the Process object
  let cli = Object.assign({}, process);

  // Add Chai-Exec properties
  cli.exitCode = process.status;
  cli.output = process.stdout + process.stderr;

  // Use Process.toString()
  cli.toString = Process.prototype.toString;

  return cli;
}
<commit_msg>Fixed a broken import<commit_after>"use strict";

const Process = require("@jsdevtools/ez-spawn/lib/process");

module.exports = normalizeResults;

/**
 * Converts an EZ-Spawn Process or ProcessError into a normalized POJO.
 *
 * @param {object} process - An EZ-Spawn Process or ProcessError
 * @returns {object} - The normalized POJO
 */
function normalizeResults (process) {
  // Copy all properties of the Process object
  let cli = Object.assign({}, process);

  // Add Chai-Exec properties
  cli.exitCode = process.status;
  cli.output = process.stdout + process.stderr;

  // Use Process.toString()
  cli.toString = Process.prototype.toString;

  return cli;
}
<|endoftext|>