<commit_before>// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '..',
    frameworks: ['jasmine', 'angular-cli'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-remap-istanbul'),
      require('angular-cli/plugins/karma')
    ],
    customLaunchers: {
      // chrome setup for travis CI using chromium
      Chrome_travis_ci: {
        base: 'Chrome',
        flags: ['--no-sandbox']
      }
    },
    files: [
      { pattern: './src/test.ts', watched: false }
    ],
    preprocessors: {
      './src/test.ts': ['angular-cli']
    },
    remapIstanbulReporter: {
      reports: {
        html: 'coverage',
        lcovonly: './coverage/coverage.lcov'
      }
    },
    angularCliConfig: './angular-cli.json',
    reporters: ['progress', 'karma-remap-istanbul'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
};
<commit_msg>fix(init): karma paths reflect sourceDir config (#1686)<commit_after>// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '..',
    frameworks: ['jasmine', 'angular-cli'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-remap-istanbul'),
      require('angular-cli/plugins/karma')
    ],
    customLaunchers: {
      // chrome setup for travis CI using chromium
      Chrome_travis_ci: {
        base: 'Chrome',
        flags: ['--no-sandbox']
      }
    },
    files: [
      { pattern: './<%= sourceDir %>/test.ts', watched: false }
    ],
    preprocessors: {
      './<%= sourceDir %>/test.ts': ['angular-cli']
    },
    remapIstanbulReporter: {
      reports: {
        html: 'coverage',
        lcovonly: './coverage/coverage.lcov'
      }
    },
    angularCliConfig: './angular-cli.json',
    reporters: ['progress', 'karma-remap-istanbul'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
};
<|endoftext|>


<commit_before>import sinon from 'sinon'
import events from 'events'
import DotReporter from '../lib/reporter'

class BaseReporter extends events.EventEmitter {
    get symbols () {
        return {}
    }

    get color () {
        return {}
    }
}

var baseReporterMock = new BaseReporter()
var reporter, logMock, printDotsMock, epilogueMock

describe('dot reporter', () => {
    beforeEach(() => {
        printDotsMock = sinon.spy()
        epilogueMock = sinon.spy()

        baseReporterMock.epilogue = epilogueMock
        baseReporterMock.printDots = printDotsMock

        reporter = new DotReporter(baseReporterMock)
    })

    it('should print \\n and call baseReporters epilogue when suite ends', () => {
        reporter.emit('end')
        epilogueMock.called.should.be.true()
    })

    it('should print pending dots for pending events', () => {
        reporter.printDots = sinon.spy()
        reporter.emit('test:pending')
        reporter.printDots.calledWith('pending').should.be.true
    })

    it('should print pass dots for passing events', () => {
        reporter.printDots = sinon.spy()
        reporter.emit('test:pass')
        reporter.printDots.calledWith('green').should.be.true
    })

    it('should print fail dots for failing events', () => {
        reporter.printDots = sinon.spy()
        reporter.emit('test:fail')
        reporter.printDots.calledWith('fail').should.be.true
    })

    it('should print pending dots for pending events', () => {
        reporter.printDots = sinon.spy()
        reporter.emit('test:pending')
        reporter.printDots.calledWith('pending').should.be.true
    })
})
<commit_msg>improved test coverage<commit_after>import sinon from 'sinon'
import events from 'events'
import DotReporter from '../lib/reporter'

class BaseReporter extends events.EventEmitter {
    get symbols () {
        return {}
    }

    get color () {
        return 'some color'
    }
}

var baseReporterMock = new BaseReporter()
var reporter, printDotsMock, epilogueMock

describe('dot reporter', () => {
    beforeEach(() => {
        printDotsMock = sinon.spy()
        epilogueMock = sinon.spy()

        baseReporterMock.epilogue = epilogueMock
        baseReporterMock.printDots = printDotsMock

        reporter = new DotReporter(baseReporterMock)
    })

    it('should print nothing when testrun starts', () => {
        reporter.printDots = sinon.spy()
        reporter.emit('start')
        reporter.printDots.calledWith(null).should.be.true
    })

    it('should print \\n and call baseReporters epilogue when suite ends', () => {
        reporter.emit('end')
        epilogueMock.called.should.be.true()
    })

    it('should print pending dots for pending events', () => {
        reporter.printDots = sinon.spy()
        reporter.emit('test:pending')
        reporter.printDots.calledWith('pending').should.be.true
    })

    it('should print pass dots for passing events', () => {
        reporter.printDots = sinon.spy()
        reporter.emit('test:pass')
        reporter.printDots.calledWith('green').should.be.true
    })

    it('should print fail dots for failing events', () => {
        reporter.printDots = sinon.spy()
        reporter.emit('test:fail')
        reporter.printDots.calledWith('fail').should.be.true
    })

    it('should print pending dots for pending events', () => {
        reporter.printDots = sinon.spy()
        reporter.emit('test:pending')
        reporter.printDots.calledWith('pending').should.be.true
    })

    it('should print nothing when test ends', () => {
        reporter.printDots = sinon.spy()
        reporter.emit('test:end')
        reporter.printDots.calledWith(null).should.be.true
    })

    it('printDots should return nothing if status is falsy', () => {
        (reporter.printDots() === undefined).should.be.true
    })
})
<|endoftext|>


<commit_before>/* eslint-env mocha */
/* @flow */

const Promise = require('bluebird')
const fse = require('fs-extra')
const _ = require('lodash')
const path = require('path')
const should = require('should')

const { scenarios, loadFSEventFiles, runActions, init } = require('../support/helpers/scenarios')
const configHelpers = require('../support/helpers/config')
const cozyHelpers = require('../support/helpers/cozy')
const { IntegrationTestHelpers } = require('../support/helpers/integration')
const pouchHelpers = require('../support/helpers/pouch')
const remoteCaptureHelpers = require('../../dev/capture/remote')

const { platform } = process

const stoppedEnvVar = 'STOPPED_CLIENT'

describe('Test scenarios', function () {
  let helpers

  before(configHelpers.createConfig)
  before(configHelpers.registerClient)
  beforeEach(pouchHelpers.createDatabase)
  beforeEach(cozyHelpers.deleteAll)
  beforeEach('set up synced dir', async function () {
    await fse.emptyDir(this.syncPath)
  })
  beforeEach('set up outside dir', async function () {
    await fse.emptyDir(path.resolve(path.join(this.syncPath, '..', 'outside')))
  })

  afterEach(pouchHelpers.cleanDatabase)
  after(configHelpers.cleanConfig)

  beforeEach(async function () {
    helpers = new IntegrationTestHelpers(this.config, this.pouch, cozyHelpers.cozy)

    // TODO: helpers.setup()
    await helpers.local.setupTrash()
    await helpers.remote.ignorePreviousChanges()
  })

  for (let scenario of scenarios) {
    if (scenario.platforms && !scenario.platforms.includes(platform)) {
      it.skip(`test/scenarios/${scenario.name}/  (skip on ${platform})`, () => {})
      continue
    }

    if (scenario.side === 'remote') {
      it.skip(`test/scenarios/${scenario.name}/local/  (skip remote only test)`, () => {})
    } else {
      for (let eventsFile of loadFSEventFiles(scenario)) {
        const localTestName = `test/scenarios/${scenario.name}/local/${eventsFile.name}`
        if (eventsFile.disabled) {
          it.skip(`${localTestName}  (${eventsFile.disabled})`, () => {})
          continue
        }

        let breakpoints = []
        if (eventsFile.events[0] && eventsFile.events[0].breakpoints) {
          breakpoints = eventsFile.events[0].breakpoints
          eventsFile.events = eventsFile.events.slice(1)
        } else {
          // break between each events
          for (let i = 0; i < eventsFile.events.length; i++) breakpoints.push(i)
        }

        if (process.env.NO_BREAKPOINTS) breakpoints = [0]

        for (let flushAfter of breakpoints) {
          it(localTestName + ' flushAfter=' + flushAfter, async function () {
            await runLocalChokidar(scenario, eventsFile, flushAfter, helpers)
          })
        }
      }

      const stoppedTestName = `test/scenarios/${scenario.name}/local/stopped`
      const stoppedTestSkipped = shouldSkipLocalStopped(scenario)
      if (stoppedTestSkipped) {
        it.skip(`${stoppedTestName} (${stoppedTestSkipped})`, () => {})
      } else {
        it(stoppedTestName, async function () {
          this.timeout(3 * 60 * 1000)
          await runLocalStopped(scenario, helpers)
        })
      }
    }

    const remoteTestName = `test/scenarios/${scenario.name}/remote/`
    const remoteTestSkipped = shouldSkipRemote(scenario)
    if (remoteTestSkipped) {
      it.skip(`${remoteTestName}  (${remoteTestSkipped})`, () => {})
      continue
    }

    it(remoteTestName, async function () {
      await runRemote(scenario, helpers)
    })
  }
})

function shouldSkipLocalStopped (scenario, env = process.env) {
  if (scenario.disabled) {
    return scenario.disabled
  } else if (env[stoppedEnvVar] == null) {
    return `${stoppedEnvVar} is not set`
  }
}

function shouldSkipRemote (scenario) {
  if (scenario.name.indexOf('outside') !== -1) {
    return 'skip outside case'
  } else if (scenario.disabled) {
    return scenario.disabled
  } else if (scenario.side === 'local') {
    return 'skip local only test'
  }
}

async function runLocalChokidar (scenario, eventsFile, flushAfter, helpers) {
  if (scenario.init) {
    let relpathFix = _.identity
    if (process.platform === 'win32' && eventsFile.name.match(/win32/)) {
      relpathFix = (relpath) => relpath.replace(/\//g, '\\')
    }
    await init(scenario, helpers._pouch, helpers.local.syncDir.abspath, relpathFix)
  }

  const eventsBefore = eventsFile.events.slice(0, flushAfter)
  const eventsAfter = eventsFile.events.slice(flushAfter)

  await runActions(scenario, helpers.local.syncDir.abspath, {skipWait: true})
  await helpers.local.simulateEvents(eventsBefore)
  await helpers.syncAll()
  await helpers.local.simulateEvents(eventsAfter)
  await helpers.syncAll()
  await helpers.remote.pullChanges()
  await helpers.syncAll()

  await verifyExpectations(scenario, helpers, {includeRemoteTrash: true})

  // TODO: pull
}

async function runLocalStopped (scenario, helpers) {
  // TODO: Find why we need this to prevent random failures and fix it.
  await Promise.delay(500)
  if (scenario.init) {
    let relpathFix = _.identity
    if (process.platform === 'win32' && scenario.name.match(/win32/)) {
      relpathFix = (relpath) => relpath.replace(/\//g, '\\')
    }
    await init(scenario, helpers._pouch, helpers.local.syncDir.abspath, relpathFix, true)
  }

  await runActions(scenario, helpers.local.syncDir.abspath, {skipWait: true})

  await helpers.local.local.watcher.start()
  await helpers.local.local.watcher.stop(true)

  await helpers.syncAll()

  await verifyExpectations(scenario, helpers, {includeRemoteTrash: true})
}

async function runRemote (scenario, helpers) {
  if (scenario.init) {
    let relpathFix = _.identity
    if (process.platform === 'win32') {
      relpathFix = (relpath) => relpath.replace(/\//g, '\\')
    }
    await init(scenario, helpers._pouch, helpers.local.syncDir.abspath, relpathFix)
    await helpers.remote.ignorePreviousChanges()
  }

  await remoteCaptureHelpers.runActions(scenario, cozyHelpers.cozy)

  await helpers.remote.pullChanges()
  // TODO: Don't sync when scenario doesn't have target FS/trash assertions?
  for (let i = 0; i < scenario.actions.length + 1; i++) {
    await helpers.syncAll()
  }

  await verifyExpectations(scenario, helpers, {includeRemoteTrash: false})

  // TODO: Local trash assertions
}

async function verifyExpectations (scenario, helpers, {includeRemoteTrash}) {
  // TODO: Wrap in custom expectation
  if (scenario.expected) {
    const expectedLocalTree = scenario.expected.tree || scenario.expected.localTree
    const expectedRemoteTree = scenario.expected.tree || scenario.expected.remoteTree
    const expected = includeRemoteTrash ? _.pick(scenario.expected, ['remoteTrash']) : {}
    const actual = {}

    if (expectedLocalTree) {
      expected.localTree = expectedLocalTree
      actual.localTree = await helpers.local.treeWithoutTrash()
    }
    if (expectedRemoteTree) {
      expected.remoteTree = expectedRemoteTree
      actual.remoteTree = await helpers.remote.treeWithoutTrash()
    }
    if (includeRemoteTrash && scenario.expected.remoteTrash) {
      actual.remoteTrash = await helpers.remote.trash()
    }
    if (scenario.expected.contents) {
      expected.localContents = scenario.expected.contents
      expected.remoteContents = scenario.expected.contents
      actual.localContents = {}
      actual.remoteContents = {}
      for (const relpath of _.keys(scenario.expected.contents)) {
        actual.localContents[relpath] = await helpers.local.readFile(relpath)
            .catch(err => `Error Reading Local(${relpath}): ${err.message}`)
        actual.remoteContents[relpath] = await helpers.remote.readFile(relpath)
            .catch(err => `Error Reading Remote(${relpath}): ${err.message}`)
      }
    }

    should(actual).deepEqual(expected)
  }
}
<commit_msg>test/scenarios/run: Extract injectChokidarBreakpoints()<commit_after>/* eslint-env mocha */
/* @flow */

const Promise = require('bluebird')
const fse = require('fs-extra')
const _ = require('lodash')
const path = require('path')
const should = require('should')

const { scenarios, loadFSEventFiles, runActions, init } = require('../support/helpers/scenarios')
const configHelpers = require('../support/helpers/config')
const cozyHelpers = require('../support/helpers/cozy')
const { IntegrationTestHelpers } = require('../support/helpers/integration')
const pouchHelpers = require('../support/helpers/pouch')
const remoteCaptureHelpers = require('../../dev/capture/remote')

const { platform } = process

const stoppedEnvVar = 'STOPPED_CLIENT'

describe('Test scenarios', function () {
  let helpers

  before(configHelpers.createConfig)
  before(configHelpers.registerClient)
  beforeEach(pouchHelpers.createDatabase)
  beforeEach(cozyHelpers.deleteAll)
  beforeEach('set up synced dir', async function () {
    await fse.emptyDir(this.syncPath)
  })
  beforeEach('set up outside dir', async function () {
    await fse.emptyDir(path.resolve(path.join(this.syncPath, '..', 'outside')))
  })

  afterEach(pouchHelpers.cleanDatabase)
  after(configHelpers.cleanConfig)

  beforeEach(async function () {
    helpers = new IntegrationTestHelpers(this.config, this.pouch, cozyHelpers.cozy)

    // TODO: helpers.setup()
    await helpers.local.setupTrash()
    await helpers.remote.ignorePreviousChanges()
  })

  for (let scenario of scenarios) {
    if (scenario.platforms && !scenario.platforms.includes(platform)) {
      it.skip(`test/scenarios/${scenario.name}/  (skip on ${platform})`, () => {})
      continue
    }

    if (scenario.side === 'remote') {
      it.skip(`test/scenarios/${scenario.name}/local/  (skip remote only test)`, () => {})
    } else {
      for (let eventsFile of loadFSEventFiles(scenario)) {
        const localTestName = `test/scenarios/${scenario.name}/local/${eventsFile.name}`
        if (eventsFile.disabled) {
          it.skip(`${localTestName}  (${eventsFile.disabled})`, () => {})
          continue
        }

        const breakpoints = injectChokidarBreakpoints(eventsFile)

        for (let flushAfter of breakpoints) {
          it(localTestName + ' flushAfter=' + flushAfter, async function () {
            await runLocalChokidar(scenario, eventsFile, flushAfter, helpers)
          })
        }
      }

      const stoppedTestName = `test/scenarios/${scenario.name}/local/stopped`
      const stoppedTestSkipped = shouldSkipLocalStopped(scenario)
      if (stoppedTestSkipped) {
        it.skip(`${stoppedTestName} (${stoppedTestSkipped})`, () => {})
      } else {
        it(stoppedTestName, async function () {
          this.timeout(3 * 60 * 1000)
          await runLocalStopped(scenario, helpers)
        })
      }
    }

    const remoteTestName = `test/scenarios/${scenario.name}/remote/`
    const remoteTestSkipped = shouldSkipRemote(scenario)
    if (remoteTestSkipped) {
      it.skip(`${remoteTestName}  (${remoteTestSkipped})`, () => {})
      continue
    }

    it(remoteTestName, async function () {
      await runRemote(scenario, helpers)
    })
  }
})

function shouldSkipLocalStopped (scenario, env = process.env) {
  if (scenario.disabled) {
    return scenario.disabled
  } else if (env[stoppedEnvVar] == null) {
    return `${stoppedEnvVar} is not set`
  }
}

function shouldSkipRemote (scenario) {
  if (scenario.name.indexOf('outside') !== -1) {
    return 'skip outside case'
  } else if (scenario.disabled) {
    return scenario.disabled
  } else if (scenario.side === 'local') {
    return 'skip local only test'
  }
}

function injectChokidarBreakpoints (eventsFile) {
  let breakpoints = []
  if (eventsFile.events[0] && eventsFile.events[0].breakpoints) {
    breakpoints = eventsFile.events[0].breakpoints
    eventsFile.events = eventsFile.events.slice(1)
  } else {
    // break between each events
    for (let i = 0; i < eventsFile.events.length; i++) breakpoints.push(i)
  }

  if (process.env.NO_BREAKPOINTS) breakpoints = [0]
  return breakpoints
}

async function runLocalChokidar (scenario, eventsFile, flushAfter, helpers) {
  if (scenario.init) {
    let relpathFix = _.identity
    if (process.platform === 'win32' && eventsFile.name.match(/win32/)) {
      relpathFix = (relpath) => relpath.replace(/\//g, '\\')
    }
    await init(scenario, helpers._pouch, helpers.local.syncDir.abspath, relpathFix)
  }

  const eventsBefore = eventsFile.events.slice(0, flushAfter)
  const eventsAfter = eventsFile.events.slice(flushAfter)

  await runActions(scenario, helpers.local.syncDir.abspath, {skipWait: true})
  await helpers.local.simulateEvents(eventsBefore)
  await helpers.syncAll()
  await helpers.local.simulateEvents(eventsAfter)
  await helpers.syncAll()
  await helpers.remote.pullChanges()
  await helpers.syncAll()

  await verifyExpectations(scenario, helpers, {includeRemoteTrash: true})

  // TODO: pull
}

async function runLocalStopped (scenario, helpers) {
  // TODO: Find why we need this to prevent random failures and fix it.
  await Promise.delay(500)
  if (scenario.init) {
    let relpathFix = _.identity
    if (process.platform === 'win32' && scenario.name.match(/win32/)) {
      relpathFix = (relpath) => relpath.replace(/\//g, '\\')
    }
    await init(scenario, helpers._pouch, helpers.local.syncDir.abspath, relpathFix, true)
  }

  await runActions(scenario, helpers.local.syncDir.abspath, {skipWait: true})

  await helpers.local.local.watcher.start()
  await helpers.local.local.watcher.stop(true)

  await helpers.syncAll()

  await verifyExpectations(scenario, helpers, {includeRemoteTrash: true})
}

async function runRemote (scenario, helpers) {
  if (scenario.init) {
    let relpathFix = _.identity
    if (process.platform === 'win32') {
      relpathFix = (relpath) => relpath.replace(/\//g, '\\')
    }
    await init(scenario, helpers._pouch, helpers.local.syncDir.abspath, relpathFix)
    await helpers.remote.ignorePreviousChanges()
  }

  await remoteCaptureHelpers.runActions(scenario, cozyHelpers.cozy)

  await helpers.remote.pullChanges()
  // TODO: Don't sync when scenario doesn't have target FS/trash assertions?
  for (let i = 0; i < scenario.actions.length + 1; i++) {
    await helpers.syncAll()
  }

  await verifyExpectations(scenario, helpers, {includeRemoteTrash: false})

  // TODO: Local trash assertions
}

async function verifyExpectations (scenario, helpers, {includeRemoteTrash}) {
  // TODO: Wrap in custom expectation
  if (scenario.expected) {
    const expectedLocalTree = scenario.expected.tree || scenario.expected.localTree
    const expectedRemoteTree = scenario.expected.tree || scenario.expected.remoteTree
    const expected = includeRemoteTrash ? _.pick(scenario.expected, ['remoteTrash']) : {}
    const actual = {}

    if (expectedLocalTree) {
      expected.localTree = expectedLocalTree
      actual.localTree = await helpers.local.treeWithoutTrash()
    }
    if (expectedRemoteTree) {
      expected.remoteTree = expectedRemoteTree
      actual.remoteTree = await helpers.remote.treeWithoutTrash()
    }
    if (includeRemoteTrash && scenario.expected.remoteTrash) {
      actual.remoteTrash = await helpers.remote.trash()
    }
    if (scenario.expected.contents) {
      expected.localContents = scenario.expected.contents
      expected.remoteContents = scenario.expected.contents
      actual.localContents = {}
      actual.remoteContents = {}
      for (const relpath of _.keys(scenario.expected.contents)) {
        actual.localContents[relpath] = await helpers.local.readFile(relpath)
            .catch(err => `Error Reading Local(${relpath}): ${err.message}`)
        actual.remoteContents[relpath] = await helpers.remote.readFile(relpath)
            .catch(err => `Error Reading Remote(${relpath}): ${err.message}`)
      }
    }

    should(actual).deepEqual(expected)
  }
}
<|endoftext|>


<commit_before>/*
 * This object's keys are (semver) version numbers, and the
 * values are arrays of objects each with a "selectors"
 * array and a "message" string.
 */
const versionDeprecations = {
  '14.0.0': [
    {
      selectors: ['.UnderlineNav-item.selected', '.UnderlineNav-item.selected .UnderlineNav-octicon'],
      message: `Please use aria-selected="true" to indicate the selected state of an UnderlineNav item.`
    },
    {
      variables: ['$status-pending'],
      message: `This variable is deprecated.`
    },
    {
      variables: ['$highlight-yellow'],
      message: `This variable is deprecated.`
    },
    {
      variables: ['$repo-private-text', '$repo-private-bg', '$repo-private-icon'],
      message: `These variables are deprecated.`
    },
    {
      variables: ['$marketingSpacers', '$allSpacers'],
      message: `Please use the $marketing-spacers and $marketing-all-spacers variables.`
    },
    {
      variables: ['$exploregrid-item-border-radius'],
      message: `This variable is deprecated. Use "4px" instead.`
    },
    {
      variables: ['$stats-switcher-py', '$stats-viewport-height'],
      message: `These variables are deprecated.`
    },
    {
      variables: ['$min_tab_size', '$max_tab_size'],
      message: `These variables are deprecated.`
    }
  ],
  '13.0.0': [
    {
      selectors: [
        '.btn-purple',
        '.btn-purple:focus',
        '.btn-purple.focus',
        '.btn-purple:hover',
        '.btn-purple.hover',
        '.btn-purple:active',
        '.btn-purple.selected',
        '[open]>.btn-purple',
        '.btn-purple:disabled',
        '.btn-purple.disabled',
        '.btn-purple .Counter'
      ],
      message: `Please don't make purple buttons.`
    },
    {
      selectors: ['.text-pending'],
      message: `Please use the "text-yellow" class instead of "text-pending".`
    },
    {
      selectors: ['.bg-pending'],
      message: `Please use the "bg-yellow-dark" class instead of "bg-pending".`
    },
    {
      selectors: [
        '.container',
        '.container::before',
        '.container::after',
        '.columns',
        '.columns::before',
        '.columns::after',
        '.column',
        '.one-third',
        '.two-thirds',
        '.one-fourth',
        '.one-half',
        '.three-fourths',
        '.one-fifth',
        '.four-fifths'
      ],
      message: `Please use [grid classes](https://primer.style/css/objects/grid).`
    },
    {
      selectors: ['.centered'],
      message: `You can use the "mx-auto" class to center any element.`
    },
    {
      selectors: [
        '.dropdown-menu-content',
        '.dropdown.active .dropdown-menu-content',
        '.dropdown-menu-content.anim-scale-in'
      ],
      message: `The "dropdown-menu-content" class is unnecessary.`
    }
  ]
}

const {version: CURRENT_VERSION} = require('./package.json')
const semver = require('semver')

// map selectors to the version and message of their deprecation
const selectorDeprecations = new Map()
const variableDeprecations = new Map()
for (const [version, deps] of Object.entries(versionDeprecations)) {
  for (const {selectors = [], variables = [], message} of deps) {
    for (const selector of selectors) {
      selectorDeprecations.set(selector, {version, message})
    }
    for (const variable of variables) {
      variableDeprecations.set(variable, {version, message})
    }
  }
}

function isSelectorDeprecated(selector, version = CURRENT_VERSION) {
  const deprecation = selectorDeprecations.get(selector)
  return deprecation ? semver.gte(deprecation.version, version) : false
}

function isVariableDeprecated(variable, version = CURRENT_VERSION) {
  const deprecation = variableDeprecations.get(variable)
  return deprecation ? semver.gte(deprecation.version, version) : false
}

module.exports = {
  versionDeprecations,
  selectorDeprecations,
  variableDeprecations,
  isSelectorDeprecated,
  isVariableDeprecated
}
<commit_msg>Deprecate .flex-item-equal<commit_after>/*
 * This object's keys are (semver) version numbers, and the
 * values are arrays of objects each with a "selectors"
 * array and a "message" string.
 */
const versionDeprecations = {
  '14.0.0': [
    {
      selectors: ['.flex-item-equal', '.flex-sm-item-equal', '.flex-md-item-equal', '.flex-lg-item-equal', '.flex-xl-item-equal'],
      message: `This variable is deprecated. Use "flex-1" instead.`
    },
    {
      selectors: ['.UnderlineNav-item.selected', '.UnderlineNav-item.selected .UnderlineNav-octicon'],
      message: `Please use aria-selected="true" to indicate the selected state of an UnderlineNav item.`
    },
    {
      variables: ['$status-pending'],
      message: `This variable is deprecated.`
    },
    {
      variables: ['$highlight-yellow'],
      message: `This variable is deprecated.`
    },
    {
      variables: ['$repo-private-text', '$repo-private-bg', '$repo-private-icon'],
      message: `These variables are deprecated.`
    },
    {
      variables: ['$marketingSpacers', '$allSpacers'],
      message: `Please use the $marketing-spacers and $marketing-all-spacers variables.`
    },
    {
      variables: ['$exploregrid-item-border-radius'],
      message: `This variable is deprecated. Use "4px" instead.`
    },
    {
      variables: ['$stats-switcher-py', '$stats-viewport-height'],
      message: `These variables are deprecated.`
    },
    {
      variables: ['$min_tab_size', '$max_tab_size'],
      message: `These variables are deprecated.`
    }
  ],
  '13.0.0': [
    {
      selectors: [
        '.btn-purple',
        '.btn-purple:focus',
        '.btn-purple.focus',
        '.btn-purple:hover',
        '.btn-purple.hover',
        '.btn-purple:active',
        '.btn-purple.selected',
        '[open]>.btn-purple',
        '.btn-purple:disabled',
        '.btn-purple.disabled',
        '.btn-purple .Counter'
      ],
      message: `Please don't make purple buttons.`
    },
    {
      selectors: ['.text-pending'],
      message: `Please use the "text-yellow" class instead of "text-pending".`
    },
    {
      selectors: ['.bg-pending'],
      message: `Please use the "bg-yellow-dark" class instead of "bg-pending".`
    },
    {
      selectors: [
        '.container',
        '.container::before',
        '.container::after',
        '.columns',
        '.columns::before',
        '.columns::after',
        '.column',
        '.one-third',
        '.two-thirds',
        '.one-fourth',
        '.one-half',
        '.three-fourths',
        '.one-fifth',
        '.four-fifths'
      ],
      message: `Please use [grid classes](https://primer.style/css/objects/grid).`
    },
    {
      selectors: ['.centered'],
      message: `You can use the "mx-auto" class to center any element.`
    },
    {
      selectors: [
        '.dropdown-menu-content',
        '.dropdown.active .dropdown-menu-content',
        '.dropdown-menu-content.anim-scale-in'
      ],
      message: `The "dropdown-menu-content" class is unnecessary.`
    }
  ]
}

const {version: CURRENT_VERSION} = require('./package.json')
const semver = require('semver')

// map selectors to the version and message of their deprecation
const selectorDeprecations = new Map()
const variableDeprecations = new Map()
for (const [version, deps] of Object.entries(versionDeprecations)) {
  for (const {selectors = [], variables = [], message} of deps) {
    for (const selector of selectors) {
      selectorDeprecations.set(selector, {version, message})
    }
    for (const variable of variables) {
      variableDeprecations.set(variable, {version, message})
    }
  }
}

function isSelectorDeprecated(selector, version = CURRENT_VERSION) {
  const deprecation = selectorDeprecations.get(selector)
  return deprecation ? semver.gte(deprecation.version, version) : false
}

function isVariableDeprecated(variable, version = CURRENT_VERSION) {
  const deprecation = variableDeprecations.get(variable)
  return deprecation ? semver.gte(deprecation.version, version) : false
}

module.exports = {
  versionDeprecations,
  selectorDeprecations,
  variableDeprecations,
  isSelectorDeprecated,
  isVariableDeprecated
}
<|endoftext|>


<commit_before>'use strict';

var fs      = require('fs');
var hljs    = require('../../build');
var jsdom   = require('jsdom').jsdom;
var utility = require('../utility');

var filename = utility.buildPath('index.html'),
    page     = fs.readFileSync(filename, 'utf-8');

// Allows hljs to use document
global.document = jsdom(page);

// Setup hljs environment
hljs.configure({ tabReplace: '    ' });
hljs.initHighlighting();

describe('special cases test', function() {
  require('./explicitLanguage');
  require('./customMarkup');
  require('./languageAlias');
  require('./noHighlight');
  require('./subLanguages');
  require('./buildClassName');
});
<commit_msg>Add br tests to special test group<commit_after>'use strict';

var _       = require('lodash');
var fs      = require('fs');
var hljs    = require('../../build');
var jsdom   = require('jsdom').jsdom;
var utility = require('../utility');

var blocks,
    filename = utility.buildPath('index.html'),
    page     = fs.readFileSync(filename, 'utf-8');

// Allows hljs to use document
global.document = jsdom(page);

// Setup hljs environment
hljs.configure({ tabReplace: '    ' });
hljs.initHighlighting();

// Setup hljs for non-`<pre><code>` tests
hljs.configure({ useBR: true });

blocks = document.querySelectorAll('.code');
_.each(blocks, hljs.highlightBlock);

describe('special cases test', function() {
  require('./explicitLanguage');
  require('./customMarkup');
  require('./languageAlias');
  require('./noHighlight');
  require('./subLanguages');
  require('./buildClassName');
  require('./useBr');
});
<|endoftext|>


<commit_before>var fs = require('fs'),
	path = require('path'),
	wrench = require('wrench'),
	TU = require('../lib/testUtils'),
	CONST = require('../../Alloy/common/constants'),
	_ = require('../../Alloy/lib/alloy/underscore')._,
	platforms = require('../../platforms/index');

var TIMEOUT_COMPILE = 10000;

var alloyRoot = path.join(__dirname,'..','..'),
	paths = {
		apps: path.join(alloyRoot,'test','apps'),
		harness: path.join(alloyRoot,'test','projects','Harness')
	},
	compilerDirectives = (function() {
		var cds = [];
		_.each(_.keys(platforms), function(p) {
			cds.push('OS_' + p.toUpperCase());
		});
		cds.concat(_.pluck(CONST.DEPLOY_TYPES,'key'));
		return cds;
	})(),
	cdRegex = new RegExp('(?:' + compilerDirectives.join('|') + ')');

// The alloy command test suite
describe('alloy compile', function() {
	// Iterate through each test app and make sure it compiles for all platforms
	_.each(wrench.readdirSyncRecursive(paths.apps), function(file) {
		var indexXml = path.join(paths.apps,file,'views','index.xml');
		if (path.existsSync(indexXml)) {
			it('preparing test app "' + file + '"', function() {
				TU.asyncExecTest('jake app:setup dir=' + file + ' quiet=1');
			});

			_.each(platforms, function(platform,k) {
				it('compiles "' + file + '" test app for platform [' + platform.platform + ']',
					function() {
						TU.asyncExecTest(
							'alloy compile ' + paths.harness + ' --config platform=' + platform.platform, {
							test: function() {
								var o = this.output;

								// Make sure there were no compile errors
								expect(o.error).toBeFalsy();
							},
							timeout: TIMEOUT_COMPILE
						}
					);
				});

				it('leaves no compiler directives in generated code', function() {
					var hrDir = path.join(paths.harness,'Resources');
					var cPaths = [
						path.join(hrDir,'alloy'),
						path.join(hrDir,platform.titaniumFolder,'alloy')
					];

					_.each(cPaths, function(cPath) {
						if (!fs.existsSync(cPath)) { return; }
						var files = wrench.readdirSyncRecursive(cPath);
						_.each(files, function(file) {
							var fullpath = path.join(cPath,file);
							if (!fs.statSync(fullpath).isFile() ||
								!/\.js$/.test(fullpath)) {
								return;
							}
							var content = fs.readFileSync(fullpath, 'utf8');
							expect(cdRegex.test(content)).toBeFalsy();
						});
					});

				});
			});
		}
	});
});
<commit_msg>use cleaner description for compile unit tests<commit_after>var fs = require('fs'),
	path = require('path'),
	wrench = require('wrench'),
	colors = require('colors'),
	TU = require('../lib/testUtils'),
	CONST = require('../../Alloy/common/constants'),
	_ = require('../../Alloy/lib/alloy/underscore')._,
	platforms = require('../../platforms/index');

var TIMEOUT_COMPILE = 10000;

var alloyRoot = path.join(__dirname,'..','..'),
	paths = {
		apps: path.join(alloyRoot,'test','apps'),
		harness: path.join(alloyRoot,'test','projects','Harness')
	},
	compilerDirectives = (function() {
		var cds = [];
		_.each(_.keys(platforms), function(p) {
			cds.push('OS_' + p.toUpperCase());
		});
		cds.concat(_.pluck(CONST.DEPLOY_TYPES,'key'));
		return cds;
	})(),
	cdRegex = new RegExp('(?:' + compilerDirectives.join('|') + ')');

// The alloy command test suite
describe('alloy compile', function() {
	// Iterate through each test app and make sure it compiles for all platforms
	_.each(wrench.readdirSyncRecursive(paths.apps), function(file) {
		describe(file.yellow, function() {
			var indexJs = path.join(paths.apps,file,'controllers','index.js');
			if (path.existsSync(indexJs)) {
				it('preparing test app', function() {
					TU.asyncExecTest('jake app:setup dir=' + file + ' quiet=1');
				});

				_.each(platforms, function(platform,k) {
					describe('for ' + platform.platform.cyan, function () {
						it('compiles without critical error',
							function() {
								TU.asyncExecTest(
									'alloy compile ' + paths.harness + ' --config platform=' + platform.platform, {
									test: function() {
										var o = this.output;

										// Make sure there were no compile errors
										expect(o.error).toBeFalsy();
									},
									timeout: TIMEOUT_COMPILE
								}
							);
						});

						it('leaves no compiler directives in generated code', function() {
							var hrDir = path.join(paths.harness,'Resources');
							var cPaths = [
								path.join(hrDir,'alloy'),
								path.join(hrDir,platform.titaniumFolder,'alloy')
							];

							_.each(cPaths, function(cPath) {
								if (!fs.existsSync(cPath)) { return; }
								var files = wrench.readdirSyncRecursive(cPath);
								_.each(files, function(file) {
									var fullpath = path.join(cPath,file);
									if (!fs.statSync(fullpath).isFile() ||
										!/\.js$/.test(fullpath)) {
										return;
									}
									var content = fs.readFileSync(fullpath, 'utf8');
									expect(cdRegex.test(content)).toBeFalsy();
								});
							});
						});
					});
				});
			}
		});
	});
});
<|endoftext|>


<commit_before>'use strict';

var logger = require('winston');
var fs = require('fs');
var path = require('path');
var co = require('co');
var message = require('./message');

var app = require.main.exports;
var bot = app.bot;
var config = app.config;

var commands = {};

function load(d) {

    var dir = path.join(d, 'commands');
    // load up the commands
    fs
        .readdirSync(dir)
        .filter(function (file) {
            // only load up named commands
            return (file.indexOf('.') !== 0) && (file !== 'index.js');
        })
        .forEach(function (file) {
            var filePath = path.join(dir, file);
            logger.debug('checking command file: %s', filePath);
            var cmd = require(filePath);
            if (!cmd) {
                return logger.error('failed to load command file: %s', filePath)
            }

            if (cmd.disabled) return;

            if (cmd.name) {
                // maybe we should check if the command already exists?
                logger.verbose('loading command %s', cmd.name);
                commands[cmd.name] = cmd;
            }
            // sort out the aliases
            if (cmd.alias) {
                cmd.alias.forEach(function (alias) {
                    logger.debug("adding alias '%s' for command '%s'", alias, cmd.name);
                    commands[alias] = cmd;
                })
            }
        });

}

// add the help command

function help(cmd) {
    return co(function* () {

        var msg = cmd.msg;
        var args = cmd.args;

        if(args.length > 0) {
            var c = args[0];
            if(!commands[c]) {
                return bot.sendMessage(msg, "command `"+c+"` not recognised");
            }

            return bot.sendMessage(msg, commandHelp(c).join("\n"));
        }

        var toSend = [];
        Object.keys(commands).sort().forEach(function (name) {

            if(name !== commands[name].name) return; // skip aliases
            if(commands[name].admin && !isAdmin(msg)) return; // skip admin commands for non-admin

            toSend = toSend.concat(commandHelp(name));

        });

        return bot.sendMessage(msg, toSend.join("\n"));

    });
}

function commandHelp(name) {
    var toSend = [];
    toSend.push("**" + name + "** - " + commands[name].desc);
    if (commands[name].alias.length > 0) {
        toSend.push("\t_aliases: " + commands[name].alias.join(" | ") + "_");
    }
    if (commands[name].usage) {
        toSend.push("\tusage: " +
            // if we have an array, then just join everything with \n
            (Array.isArray(commands[name].usage) ? commands[name].usage.join("\n") : commands[name].usage)
        );
    }
    return toSend;
}

commands.help = {
    desc: 'List commands',
    name: 'help',
    usage: '`help [command]`',
    alias: ['h'],
    exec: help
};

bot.on("messageUpdated", function (msg0, msg1) {
    parseMessage(msg1);
});

bot.on("message", parseMessage);

function parseMessage(msg) {

    co(function* () {

        if (msg.author.id === bot.user.id) return;
        if (msg.author.bot) return;

        // look for the command prefix
        if(!msg.content.toLowerCase().startsWith(config.commandPrefix)) return;

        logger.debug("got message from [%s] in channel [%s]: ",
            msg.author.username, (msg.channel.name || "PM"), msg.content);

        //strip off the prefix and split into args
        var args = msg.content.substring(config.commandPrefix.length).trim().match(/[^\s]+|"(?:\\"|[^"])+"/g);
        var cmdName = args.shift().toLowerCase();

        logger.debug("found command '%s'", cmdName);

        // yep, ok then see if we have that command loaded
        if(!commands[cmdName] || !commands[cmdName].exec) return;

        var cmd = {
            msg: msg,
            args: args,
            name: cmdName,
            isPublic : false
        };

        // check if the last argument was public
        if(args.length && (args[args.length - 1].toLowerCase() === 'public')) {
            cmd.isPublic = !msg.channel.isPrivate;
            args.length--;
        }

        // check if it's an admin command
        if(commands[cmdName].admin && !isAdmin(msg)) {
            // see if we have the admin role
            return message.send(msg, "You are not authorised to run `"+cmdName+"`", cmd.isPublic, 10000);
        }

        logger.debug("executing command [%s] with args [%s]", cmdName, args.join(","));
        // all looks good, so let's run the command
        yield commands[cmdName].exec(cmd);

    }).catch(function (err) {
        logger.error("Error when parsing msg '"+msg+"':"+err);
        message.send(msg, "Oops, something went unexpectedly wrong\n```"+err+"```", !msg.channel.isPrivate, 10000);
    });

}

function isAdmin(msg) {
    var server = msg.server || app.defaultServer;
    var role = server.roles.get("name", config.discord.adminRole);
    if(!role) return false;

    return msg.author.hasRole(role);
}

module.exports.load = load;<commit_msg>Modified help to use message.send()<commit_after>'use strict';

var logger = require('winston');
var fs = require('fs');
var path = require('path');
var co = require('co');
var message = require('./message');

var app = require.main.exports;
var bot = app.bot;
var config = app.config;

var commands = {};

function load(d) {

    var dir = path.join(d, 'commands');
    // load up the commands
    fs
        .readdirSync(dir)
        .filter(function (file) {
            // only load up named commands
            return (file.indexOf('.') !== 0) && (file !== 'index.js');
        })
        .forEach(function (file) {
            var filePath = path.join(dir, file);
            logger.debug('checking command file: %s', filePath);
            var cmd = require(filePath);
            if (!cmd) {
                return logger.error('failed to load command file: %s', filePath)
            }

            if (cmd.disabled) return;

            if (cmd.name) {
                // maybe we should check if the command already exists?
                logger.verbose('loading command %s', cmd.name);
                commands[cmd.name] = cmd;
            }
            // sort out the aliases
            if (cmd.alias) {
                cmd.alias.forEach(function (alias) {
                    logger.debug("adding alias '%s' for command '%s'", alias, cmd.name);
                    commands[alias] = cmd;
                })
            }
        });

}

// add the help command

function help(cmd) {
    return co(function* () {

        var msg = cmd.msg;
        var args = cmd.args;

        if(args.length > 0) {
            var c = args[0];
            if(!commands[c]) {
                return message.send(msg, "command `"+c+"` not recognised", cmd.isPublic, 10000);
            }

            return message.send(msg, commandHelp(c).join("\n"), cmd.isPublic);
        }

        var toSend = [];
        Object.keys(commands).sort().forEach(function (name) {

            if(name !== commands[name].name) return; // skip aliases
            if(commands[name].admin && !isAdmin(msg)) return; // skip admin commands for non-admin

            toSend.push(commandHelp(name).join("\n"));

        });

        return message.send(msg, toSend, cmd.isPublic);

    });
}

function commandHelp(name) {
    var toSend = [];
    toSend.push("**" + name + "** - " + commands[name].desc);
    if (commands[name].alias.length > 0) {
        toSend.push("\t_aliases: " + commands[name].alias.join(" | ") + "_");
    }
    if (commands[name].usage) {
        toSend.push("\tusage: " +
            // if we have an array, then just join everything with \n
            (Array.isArray(commands[name].usage) ? commands[name].usage.join("\n") : commands[name].usage)
        );
    }
    return toSend;
}

commands.help = {
    desc: 'List commands',
    name: 'help',
    usage: '`help [command]`',
    alias: ['h'],
    exec: help
};

bot.on("messageUpdated", function (msg0, msg1) {
    parseMessage(msg1);
});

bot.on("message", parseMessage);

function parseMessage(msg) {

    co(function* () {

        if (msg.author.id === bot.user.id) return;
        if (msg.author.bot) return;

        // look for the command prefix
        if(!msg.content.toLowerCase().startsWith(config.commandPrefix)) return;

        logger.debug("got message from [%s] in channel [%s]: ",
            msg.author.username, (msg.channel.name || "PM"), msg.content);

        //strip off the prefix and split into args
        var args = msg.content.substring(config.commandPrefix.length).trim().match(/[^\s]+|"(?:\\"|[^"])+"/g);
        var cmdName = args.shift().toLowerCase();

        logger.debug("found command '%s'", cmdName);

        // yep, ok then see if we have that command loaded
        if(!commands[cmdName] || !commands[cmdName].exec) return;

        var cmd = {
            msg: msg,
            args: args,
            name: cmdName,
            isPublic : false
        };

        // check if the last argument was public
        if(args.length && (args[args.length - 1].toLowerCase() === 'public')) {
            cmd.isPublic = !msg.channel.isPrivate;
            args.length--;
        }

        // check if it's an admin command
        if(commands[cmdName].admin && !isAdmin(msg)) {
            // see if we have the admin role
            return message.send(msg, "You are not authorised to run `"+cmdName+"`", cmd.isPublic, 10000);
        }

        logger.debug("executing command [%s] with args [%s]", cmdName, args.join(","));
        // all looks good, so let's run the command
        yield commands[cmdName].exec(cmd);

    }).catch(function (err) {
        logger.error("Error when parsing msg '"+msg+"':"+err);
        message.send(msg, "Oops, something went unexpectedly wrong\n```"+err+"```", !msg.channel.isPrivate, 10000);
    });

}

function isAdmin(msg) {
    var server = msg.server || app.defaultServer;
    var role = server.roles.get("name", config.discord.adminRole);
    if(!role) return false;

    return msg.author.hasRole(role);
}

module.exports.load = load;<|endoftext|>


<commit_before>'use strict'
// This file is required by compile-cache, which is required directly from
// apm, so it can only use the subset of newer JavaScript features that apm's
// version of Node supports. Strict mode is required for block scoped declarations.

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

const minimatch = require('minimatch')

let Resolve = null

class PackageTranspilationRegistry {
  constructor () {
    this.configByPackagePath = {}
    this.specByFilePath = {}
    this.transpilerPaths = {}
  }

  addTranspilerConfigForPath (packagePath, packageName, packageMeta, config) {
    this.configByPackagePath[packagePath] = {
      name: packageName,
      meta: packageMeta,
      path: packagePath,
      specs: config.map(spec => Object.assign({}, spec))
    }
  }

  removeTranspilerConfigForPath (packagePath) {
    delete this.configByPackagePath[packagePath]
    const packagePathWithSep = packagePath.endsWith(path.sep) ?
      packagePath : packagePath + path.sep;
    Object.keys(this.specByFilePath).forEach(filePath => {
      console.log('checking if ' + filePath + ' starts with ' + packagePathWithSep)
      if (filePath.startsWith(packagePathWithSep)) {
        console.log(' >> REMOVING ' + filePath)
        delete this.specByFilePath[filePath]
      }
    })
  }

  // Wraps the transpiler in an object with the same interface
  // that falls back to the original transpiler implementation if and
  // only if a package hasn't registered its desire to transpile its own source.
  wrapTranspiler (transpiler) {
    return {
      getCachePath: (sourceCode, filePath) => {
        const spec = this.getPackageTranspilerSpecForFilePath(filePath)
        if (spec) {
          try {
            return this.getCachePath(sourceCode, filePath, spec)
          } catch (err) {
            console.log("ERROR!!!")
            console.log("The file being transpiled was " + filePath)
            console.log(spec)
          }
        }

        return transpiler.getCachePath(sourceCode, filePath)
      },

      compile: (sourceCode, filePath) => {
        const spec = this.getPackageTranspilerSpecForFilePath(filePath)
        if (spec) {
          return this.transpileWithPackageTranspiler(sourceCode, filePath, spec)
        }

        return transpiler.compile(sourceCode, filePath)
      },

      shouldCompile: (sourceCode, filePath) => {
        if (this.transpilerPaths[filePath]) {
          return false
        }
        const spec = this.getPackageTranspilerSpecForFilePath(filePath)
        if (spec) {
          return true
        }

        return transpiler.shouldCompile(sourceCode, filePath)
      }
    }
  }

  getPackageTranspilerSpecForFilePath (filePath) {
    if (this.specByFilePath[filePath] !== undefined) return this.specByFilePath[filePath]

    let thisPath = filePath
    let lastPath = null
    // Iterate parents from the file path to the root, checking at each level
    // to see if a package manages transpilation for that directory.
    // This means searching for a config for `/path/to/file/here.js` only
    // only iterates four times, even if there are hundreds of configs registered.
    while (thisPath !== lastPath) { // until we reach the root
      let config = this.configByPackagePath[thisPath]
      if (config) {
        const relativePath = path.relative(thisPath, filePath)
        if (relativePath.startsWith(`node_modules${path.sep}`) || relativePath.indexOf(`${path.sep}node_modules${path.sep}`) > -1) {
          return false
        }
        for (let i = 0; i < config.specs.length; i++) {
          const spec = config.specs[i]
          if (minimatch(filePath, path.join(config.path, spec.glob))) {
            spec._config = config
            this.specByFilePath[filePath] = spec
            return spec
          }
        }
      }

      lastPath = thisPath
      thisPath = path.join(thisPath, '..')
    }

    this.specByFilePath[filePath] = null
    return null
  }

  getCachePath (sourceCode, filePath, spec) {
    const transpilerPath = this.getTranspilerPath(spec)
    const transpilerSource = spec._transpilerSource || fs.readFileSync(transpilerPath, 'utf8')
    spec._transpilerSource = transpilerSource
    const transpiler = this.getTranspiler(spec)

    let hash = crypto
      .createHash('sha1')
      .update(JSON.stringify(spec.options || {}))
      .update(transpilerSource, 'utf8')
      .update(sourceCode, 'utf8')

    if (transpiler && transpiler.getCacheKeyData) {
      const meta = this.getMetadata(spec)
      const additionalCacheData = transpiler.getCacheKeyData(sourceCode, filePath, spec.options || {}, meta)
      hash.update(additionalCacheData, 'utf8')
    }

    return path.join('package-transpile', spec._config.name, hash.digest('hex'))
  }

  transpileWithPackageTranspiler (sourceCode, filePath, spec) {
    const transpiler = this.getTranspiler(spec)

    if (transpiler) {
      const meta = this.getMetadata(spec)
      const result = transpiler.transpile(sourceCode, filePath, spec.options || {}, meta)
      if (result === undefined || (result && result.code === undefined)) {
        return sourceCode
      } else if (result.code) {
        return result.code.toString()
      } else {
        throw new Error('Could not find a property `.code` on the transpilation results of ' + filePath)
      }
    } else {
      const err = new Error("Could not resolve transpiler '" + spec.transpiler + "' from '" + spec._config.path + "'")
      throw err
    }
  }

  getMetadata (spec) {
    return {
      name: spec._config.name,
      path: spec._config.path,
      meta: spec._config.meta
    }
  }

  getTranspilerPath (spec) {
    Resolve = Resolve || require('resolve')
    return Resolve.sync(spec.transpiler, {
      basedir: spec._config.path,
      extensions: Object.keys(require.extensions)
    })
  }

  getTranspiler (spec) {
    const transpilerPath = this.getTranspilerPath(spec)
    if (transpilerPath) {
      const transpiler = require(transpilerPath)
      this.transpilerPaths[transpilerPath] = true
      return transpiler
    }
  }
}

module.exports = PackageTranspilationRegistry
<commit_msg>Another logging<commit_after>'use strict'
// This file is required by compile-cache, which is required directly from
// apm, so it can only use the subset of newer JavaScript features that apm's
// version of Node supports. Strict mode is required for block scoped declarations.

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

const minimatch = require('minimatch')

let Resolve = null

class PackageTranspilationRegistry {
  constructor () {
    this.configByPackagePath = {}
    this.specByFilePath = {}
    this.transpilerPaths = {}
  }

  addTranspilerConfigForPath (packagePath, packageName, packageMeta, config) {
    console.log(">>>>> ADDING: " + packagePath)
    this.configByPackagePath[packagePath] = {
      name: packageName,
      meta: packageMeta,
      path: packagePath,
      specs: config.map(spec => Object.assign({}, spec))
    }
  }

  removeTranspilerConfigForPath (packagePath) {
    console.log(">>>>> REMOVING: " + packagePath)
    delete this.configByPackagePath[packagePath]
    const packagePathWithSep = packagePath.endsWith(path.sep) ?
      packagePath : packagePath + path.sep;
    Object.keys(this.specByFilePath).forEach(filePath => {
      console.log('checking if ' + filePath + ' starts with ' + packagePathWithSep)
      if (filePath.startsWith(packagePathWithSep)) {
        console.log(' >> REMOVING ' + filePath)
        delete this.specByFilePath[filePath]
      }
    })
  }

  // Wraps the transpiler in an object with the same interface
  // that falls back to the original transpiler implementation if and
  // only if a package hasn't registered its desire to transpile its own source.
  wrapTranspiler (transpiler) {
    return {
      getCachePath: (sourceCode, filePath) => {
        const spec = this.getPackageTranspilerSpecForFilePath(filePath)
        if (spec) {
          try {
            return this.getCachePath(sourceCode, filePath, spec)
          } catch (err) {
            console.log("ERROR!!!")
            console.log("The file being transpiled was " + filePath)
            console.log(spec)
          }
        }

        return transpiler.getCachePath(sourceCode, filePath)
      },

      compile: (sourceCode, filePath) => {
        const spec = this.getPackageTranspilerSpecForFilePath(filePath)
        if (spec) {
          return this.transpileWithPackageTranspiler(sourceCode, filePath, spec)
        }

        return transpiler.compile(sourceCode, filePath)
      },

      shouldCompile: (sourceCode, filePath) => {
        if (this.transpilerPaths[filePath]) {
          return false
        }
        const spec = this.getPackageTranspilerSpecForFilePath(filePath)
        if (spec) {
          return true
        }

        return transpiler.shouldCompile(sourceCode, filePath)
      }
    }
  }

  getPackageTranspilerSpecForFilePath (filePath) {
    if (this.specByFilePath[filePath] !== undefined) return this.specByFilePath[filePath]

    let thisPath = filePath
    let lastPath = null
    // Iterate parents from the file path to the root, checking at each level
    // to see if a package manages transpilation for that directory.
    // This means searching for a config for `/path/to/file/here.js` only
    // only iterates four times, even if there are hundreds of configs registered.
    while (thisPath !== lastPath) { // until we reach the root
      let config = this.configByPackagePath[thisPath]
      if (config) {
        const relativePath = path.relative(thisPath, filePath)
        if (relativePath.startsWith(`node_modules${path.sep}`) || relativePath.indexOf(`${path.sep}node_modules${path.sep}`) > -1) {
          return false
        }
        for (let i = 0; i < config.specs.length; i++) {
          const spec = config.specs[i]
          if (minimatch(filePath, path.join(config.path, spec.glob))) {
            spec._config = config
            this.specByFilePath[filePath] = spec
            return spec
          }
        }
      }

      lastPath = thisPath
      thisPath = path.join(thisPath, '..')
    }

    this.specByFilePath[filePath] = null
    return null
  }

  getCachePath (sourceCode, filePath, spec) {
    const transpilerPath = this.getTranspilerPath(spec)
    const transpilerSource = spec._transpilerSource || fs.readFileSync(transpilerPath, 'utf8')
    spec._transpilerSource = transpilerSource
    const transpiler = this.getTranspiler(spec)

    let hash = crypto
      .createHash('sha1')
      .update(JSON.stringify(spec.options || {}))
      .update(transpilerSource, 'utf8')
      .update(sourceCode, 'utf8')

    if (transpiler && transpiler.getCacheKeyData) {
      const meta = this.getMetadata(spec)
      const additionalCacheData = transpiler.getCacheKeyData(sourceCode, filePath, spec.options || {}, meta)
      hash.update(additionalCacheData, 'utf8')
    }

    return path.join('package-transpile', spec._config.name, hash.digest('hex'))
  }

  transpileWithPackageTranspiler (sourceCode, filePath, spec) {
    const transpiler = this.getTranspiler(spec)

    if (transpiler) {
      const meta = this.getMetadata(spec)
      const result = transpiler.transpile(sourceCode, filePath, spec.options || {}, meta)
      if (result === undefined || (result && result.code === undefined)) {
        return sourceCode
      } else if (result.code) {
        return result.code.toString()
      } else {
        throw new Error('Could not find a property `.code` on the transpilation results of ' + filePath)
      }
    } else {
      const err = new Error("Could not resolve transpiler '" + spec.transpiler + "' from '" + spec._config.path + "'")
      throw err
    }
  }

  getMetadata (spec) {
    return {
      name: spec._config.name,
      path: spec._config.path,
      meta: spec._config.meta
    }
  }

  getTranspilerPath (spec) {
    Resolve = Resolve || require('resolve')
    return Resolve.sync(spec.transpiler, {
      basedir: spec._config.path,
      extensions: Object.keys(require.extensions)
    })
  }

  getTranspiler (spec) {
    const transpilerPath = this.getTranspilerPath(spec)
    if (transpilerPath) {
      const transpiler = require(transpilerPath)
      this.transpilerPaths[transpilerPath] = true
      return transpiler
    }
  }
}

module.exports = PackageTranspilationRegistry
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';

import { BottomConfirmModal, DataTablePageModal } from '../widgets/modals';
import { PageButton, SearchBar, DataTablePageView, ToggleBar } from '../widgets';
import { DataTable, DataTableHeaderRow, DataTableRow } from '../widgets/DataTable';

import { UIDatabase } from '../database';
import Settings from '../settings/MobileAppSettings';
import { MODAL_KEYS, getAllPrograms } from '../utilities';
import { usePageReducer, useNavigationFocus, useSyncListener } from '../hooks';
import { createSupplierRequisition, gotoSupplierRequisition } from '../navigation/actions';
import { getItemLayout, recordKeyExtractor } from './dataTableUtilities';

import globalStyles from '../globalStyles';
import { buttonStrings, modalStrings } from '../localization';

/**
 * Renders a mSupply mobile page with a list of supplier requisitions.
 *
 * State:
 * Uses a reducer to manage state with `backingData` being a realm results
 * of items to display. `data` is a plain JS array of realm objects. data is
 * hydrated from `backingData` to display in the interface.
 * i.e: When filtering, data is populated from filtered items of `backingData`.
 *
 * dataState is a simple map of objects corresponding to a row being displayed,
 * holding the state of a given row. Each object has the shape :
 * { isSelected, isFocused },
 *
 * @prop {String} routeName     The current route name for the top of the navigation stack.
 * @prop {Object} currentUser   The currently logged in user.
 * @prop {Func}   reduxDispatch Dispatch method for the app-wide redux store.
 * @prop {Object} navigation    Reference to the main application stack navigator.
 */
export const SupplierRequisitionsPage = ({
  routeName,
  currentUser,
  dispatch: reduxDispatch,
  navigation,
}) => {
  const initialState = { page: routeName };

  const [state, dispatch, debouncedDispatch] = usePageReducer(initialState);

  const {
    data,
    dataState,
    sortBy,
    isAscending,
    modalKey,
    hasSelection,
    searchTerm,
    PageActions,
    columns,
    showFinalised,
  } = state;

  // Custom hook to refresh data on this page when becoming the head of the stack again.
  const refreshCallback = () => dispatch(PageActions.refreshData(), []);
  useNavigationFocus(refreshCallback, navigation);
  // Custom hook to listen to sync changes - refreshing data when requisitions are synced.
  useSyncListener(refreshCallback, 'Requisition');

  const usingPrograms = useMemo(() => getAllPrograms(Settings, UIDatabase).length > 0, []);
  const { SELECT_SUPPLIER, PROGRAM_REQUISITION } = MODAL_KEYS;
  const NEW_REQUISITON = usingPrograms ? PROGRAM_REQUISITION : SELECT_SUPPLIER;

  const onPressRow = useCallback(rowData => reduxDispatch(gotoSupplierRequisition(rowData)), []);
  const onConfirmDelete = () => dispatch(PageActions.deleteRequisitions());
  const onCancelDelete = () => dispatch(PageActions.deselectAll());
  const onSearchFiltering = value => dispatch(PageActions.filterData(value));
  const onNewRequisition = () => dispatch(PageActions.openModal(NEW_REQUISITON));
  const onCloseModal = () => dispatch(PageActions.closeModal());
  const onToggleShowFinalised = () => dispatch(PageActions.toggleShowFinalised(showFinalised));

  const onCreateRequisition = otherStoreName => {
    onCloseModal();
    reduxDispatch(createSupplierRequisition({ otherStoreName, currentUser }));
  };

  const onCreateProgramRequisition = requisitionParameters => {
    onCloseModal();
    reduxDispatch(createSupplierRequisition({ ...requisitionParameters, currentUser }));
  };

  const getAction = useCallback((colKey, propName) => {
    switch (colKey) {
      case 'remove':
        if (propName === 'onCheckAction') return PageActions.selectRow;
        return PageActions.deselectRow;
      default:
        return null;
    }
  }, []);

  const getModalOnSelect = () => {
    switch (modalKey) {
      case SELECT_SUPPLIER:
        return onCreateRequisition;
      case PROGRAM_REQUISITION:
        return onCreateProgramRequisition;
      default:
        return null;
    }
  };

  const renderRow = useCallback(
    listItem => {
      const { item, index } = listItem;
      const rowKey = recordKeyExtractor(item);
      return (
        <DataTableRow
          rowData={data[index]}
          rowState={dataState.get(rowKey)}
          rowKey={rowKey}
          columns={columns}
          dispatch={dispatch}
          getAction={getAction}
          onPress={onPressRow}
          rowIndex={index}
        />
      );
    },
    [data, dataState]
  );

  const renderHeader = useCallback(
    () => (
      <DataTableHeaderRow
        columns={columns}
        dispatch={debouncedDispatch}
        sortAction={PageActions.sortData}
        isAscending={isAscending}
        sortBy={sortBy}
      />
    ),
    [sortBy, isAscending]
  );

  const toggles = useMemo(
    () => [
      { text: buttonStrings.current, onPress: onToggleShowFinalised, isOn: !showFinalised },
      { text: buttonStrings.past, onPress: onToggleShowFinalised, isOn: showFinalised },
    ],
    [showFinalised]
  );

  const {
    pageTopSectionContainer,
    pageTopLeftSectionContainer,
    pageTopRightSectionContainer,
  } = globalStyles;
  return (
    <DataTablePageView>
      <View style={pageTopSectionContainer}>
        <View style={pageTopLeftSectionContainer}>
          <ToggleBar toggles={toggles} />
          <SearchBar onChangeText={onSearchFiltering} value={searchTerm} />
        </View>
        <View style={pageTopRightSectionContainer}>
          <PageButton text={buttonStrings.new_requisition} onPress={onNewRequisition} />
        </View>
      </View>
      <DataTable
        data={data}
        extraData={dataState}
        renderRow={renderRow}
        renderHeader={renderHeader}
        keyExtractor={recordKeyExtractor}
        getItemLayout={getItemLayout}
      />
      <BottomConfirmModal
        isOpen={hasSelection}
        questionText={modalStrings.remove_these_items}
        onCancel={onCancelDelete}
        onConfirm={onConfirmDelete}
        confirmText={modalStrings.remove}
      />
      <DataTablePageModal
        isOpen={!!modalKey}
        modalKey={modalKey}
        onClose={onCloseModal}
        onSelect={getModalOnSelect()}
        dispatch={dispatch}
      />
    </DataTablePageView>
  );
};

/* eslint-disable react/forbid-prop-types */
SupplierRequisitionsPage.propTypes = {
  routeName: PropTypes.string.isRequired,
  dispatch: PropTypes.func.isRequired,
  currentUser: PropTypes.object.isRequired,
  navigation: PropTypes.object.isRequired,
};

SupplierRequisitionsPage.propTypes = { routeName: PropTypes.string.isRequired };
<commit_msg>Update SupplierRequisitionsPage propTypes<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';

import { navigation } from 'react-navigation';

import { BottomConfirmModal, DataTablePageModal } from '../widgets/modals';
import { PageButton, SearchBar, DataTablePageView, ToggleBar } from '../widgets';
import { DataTable, DataTableHeaderRow, DataTableRow } from '../widgets/DataTable';

import { UIDatabase } from '../database';
import { User } from '../database/DataTypes';
import Settings from '../settings/MobileAppSettings';
import { MODAL_KEYS, getAllPrograms } from '../utilities';
import { usePageReducer, useNavigationFocus, useSyncListener } from '../hooks';
import { createSupplierRequisition, gotoSupplierRequisition } from '../navigation/actions';
import { getItemLayout, recordKeyExtractor } from './dataTableUtilities';

import globalStyles from '../globalStyles';
import { buttonStrings, modalStrings } from '../localization';

/**
 * Renders a mSupply mobile page with a list of supplier requisitions.
 *
 * State:
 * Uses a reducer to manage state with `backingData` being a realm results
 * of items to display. `data` is a plain JS array of realm objects. data is
 * hydrated from `backingData` to display in the interface.
 * i.e: When filtering, data is populated from filtered items of `backingData`.
 *
 * dataState is a simple map of objects corresponding to a row being displayed,
 * holding the state of a given row. Each object has the shape :
 * { isSelected, isFocused },
 *
 * @prop {String} routeName     The current route name for the top of the navigation stack.
 * @prop {Object} currentUser   The currently logged in user.
 * @prop {Func}   reduxDispatch Dispatch method for the app-wide redux store.
 * @prop {Object} navigation    Reference to the main application stack navigator.
 */
export const SupplierRequisitionsPage = ({
  routeName,
  currentUser,
  dispatch: reduxDispatch,
  navigation: reactNavigation,
}) => {
  const initialState = { page: routeName };

  const [state, dispatch, debouncedDispatch] = usePageReducer(initialState);

  const {
    data,
    dataState,
    sortBy,
    isAscending,
    modalKey,
    hasSelection,
    searchTerm,
    PageActions,
    columns,
    showFinalised,
  } = state;

  // Custom hook to refresh data on this page when becoming the head of the stack again.
  const refreshCallback = () => dispatch(PageActions.refreshData(), []);
  useNavigationFocus(refreshCallback, reactNavigation);
  // Custom hook to listen to sync changes - refreshing data when requisitions are synced.
  useSyncListener(refreshCallback, 'Requisition');

  const usingPrograms = useMemo(() => getAllPrograms(Settings, UIDatabase).length > 0, []);
  const { SELECT_SUPPLIER, PROGRAM_REQUISITION } = MODAL_KEYS;
  const NEW_REQUISITON = usingPrograms ? PROGRAM_REQUISITION : SELECT_SUPPLIER;

  const onPressRow = useCallback(rowData => reduxDispatch(gotoSupplierRequisition(rowData)), []);
  const onConfirmDelete = () => dispatch(PageActions.deleteRequisitions());
  const onCancelDelete = () => dispatch(PageActions.deselectAll());
  const onSearchFiltering = value => dispatch(PageActions.filterData(value));
  const onNewRequisition = () => dispatch(PageActions.openModal(NEW_REQUISITON));
  const onCloseModal = () => dispatch(PageActions.closeModal());
  const onToggleShowFinalised = () => dispatch(PageActions.toggleShowFinalised(showFinalised));

  const onCreateRequisition = otherStoreName => {
    onCloseModal();
    reduxDispatch(createSupplierRequisition({ otherStoreName, currentUser }));
  };

  const onCreateProgramRequisition = requisitionParameters => {
    onCloseModal();
    reduxDispatch(createSupplierRequisition({ ...requisitionParameters, currentUser }));
  };

  const getAction = useCallback((colKey, propName) => {
    switch (colKey) {
      case 'remove':
        if (propName === 'onCheckAction') return PageActions.selectRow;
        return PageActions.deselectRow;
      default:
        return null;
    }
  }, []);

  const getModalOnSelect = () => {
    switch (modalKey) {
      case SELECT_SUPPLIER:
        return onCreateRequisition;
      case PROGRAM_REQUISITION:
        return onCreateProgramRequisition;
      default:
        return null;
    }
  };

  const renderRow = useCallback(
    listItem => {
      const { item, index } = listItem;
      const rowKey = recordKeyExtractor(item);
      return (
        <DataTableRow
          rowData={data[index]}
          rowState={dataState.get(rowKey)}
          rowKey={rowKey}
          columns={columns}
          dispatch={dispatch}
          getAction={getAction}
          onPress={onPressRow}
          rowIndex={index}
        />
      );
    },
    [data, dataState]
  );

  const renderHeader = useCallback(
    () => (
      <DataTableHeaderRow
        columns={columns}
        dispatch={debouncedDispatch}
        sortAction={PageActions.sortData}
        isAscending={isAscending}
        sortBy={sortBy}
      />
    ),
    [sortBy, isAscending]
  );

  const toggles = useMemo(
    () => [
      { text: buttonStrings.current, onPress: onToggleShowFinalised, isOn: !showFinalised },
      { text: buttonStrings.past, onPress: onToggleShowFinalised, isOn: showFinalised },
    ],
    [showFinalised]
  );

  const {
    pageTopSectionContainer,
    pageTopLeftSectionContainer,
    pageTopRightSectionContainer,
  } = globalStyles;
  return (
    <DataTablePageView>
      <View style={pageTopSectionContainer}>
        <View style={pageTopLeftSectionContainer}>
          <ToggleBar toggles={toggles} />
          <SearchBar onChangeText={onSearchFiltering} value={searchTerm} />
        </View>
        <View style={pageTopRightSectionContainer}>
          <PageButton text={buttonStrings.new_requisition} onPress={onNewRequisition} />
        </View>
      </View>
      <DataTable
        data={data}
        extraData={dataState}
        renderRow={renderRow}
        renderHeader={renderHeader}
        keyExtractor={recordKeyExtractor}
        getItemLayout={getItemLayout}
      />
      <BottomConfirmModal
        isOpen={hasSelection}
        questionText={modalStrings.remove_these_items}
        onCancel={onCancelDelete}
        onConfirm={onConfirmDelete}
        confirmText={modalStrings.remove}
      />
      <DataTablePageModal
        isOpen={!!modalKey}
        modalKey={modalKey}
        onClose={onCloseModal}
        onSelect={getModalOnSelect()}
        dispatch={dispatch}
      />
    </DataTablePageView>
  );
};

SupplierRequisitionsPage.propTypes = {
  routeName: PropTypes.string.isRequired,
  dispatch: PropTypes.func.isRequired,
  currentUser: PropTypes.instanceOf(User).isRequired,
  navigation: PropTypes.instanceOf(navigation).isRequired,
};

SupplierRequisitionsPage.propTypes = { routeName: PropTypes.string.isRequired };
<|endoftext|>


<commit_before>/**
 *  class DirectiveProcessor
 *
 *  The `DirectiveProcessor` is responsible for parsing and evaluating
 *  directive comments in a source file.
 *
 *  A directive comment starts with a comment prefix, followed by an "=",
 *  then the directive name, then any arguments.
 *
 *  - **JavaScript one-line comments:**       `//= require "foo"
 *  - **CoffeeScript one-line comments:**     `#= require "baz"
 *  - **JavaScript and CSS block comments:**  `*= require "bar"
 *
 *  This behavior can be disabled with:
 *
 *      environment.unregisterPreProcessor('text/css', DirectiveProcessor);
 *      environment.unregisterPreProcessor('application/javascript', DirectiveProcessor);
 *
 *
 *  ##### SUBCLASS OF
 *
 *  [[Template]]
 **/

'use strict';

// stdlib
var path = require('path');

// 3rd-party
var _           = require('underscore');
var async       = require('async');
var shellwords  = require('shellwords').split;

// internal
var Template    = require('../template');
var prop        = require('../common').prop;
var getter      = require('../common').getter;
var isAbsolute  = require('../common').isAbsolute;
var isRelative  = require('../common').isRelative;

////////////////////////////////////////////////////////////////////////////////

// Returns an Array of lines.
// Originl idea of cross-platform line splitting taken from Sugr.JS:
// https://github.com/andrewplummer/Sugar/blob/f6d1c2e9/lib/string.js#L323
function get_lines(str) {
  return String(str || '').match(/^.*$/gm);
}

// Directives will only be picked up if they are in the header
// of the source file. C style (/* */), JavaScript (//), and
// Ruby (#) comments are supported.
//
// Directives in comments after the first non-whitespace line
// of code will not be processed.
var HEADER_PATTERN = new RegExp(
  '^(?:\\s*' +
    '(' +
      '(?:\/[*](?:\\s*|.+?)*?[*]\/)' + '|' +
      '(?:\/\/.*\n?)+' + '|' +
      '(?:#.*\n?)+' +
    ')*' +
  ')*', 'm');

// Directives are denoted by a `=` followed by the name, then
// argument list.
//
// A few different styles are allowed:
//
//     // =require foo
//     //= require foo
//     //= require "foo"
//
var DIRECTIVE_PATTERN = new RegExp('^[\\W]*=\\s*(\\w+.*?)([*]\/)?$');

// Real directive processors
var DIRECTIVE_HANDLERS = {
  // The `require` directive functions similar to Ruby's `require`.
  // It provides a way to declare a dependency on a file in your path
  // and ensures its only loaded once before the source file.
  //
  // `require` works with files in the environment path:
  //
  //     //= require "foo.js"
  //
  // Extensions are optional. If your source file is ".js", it
  // assumes you are requiring another ".js".
  //
  //     //= require "foo"
  //
  // Relative paths work too. Use a leading `./` to denote a relative
  // path:
  //
  //     //= require "./bar"
  //
  require: function (self, args, next) {
    var pathname = isRelative(args[0]) ? args[0] : ('./' + args[0]);

    try {
      self.context.requireAsset(pathname);
    } catch (err) {
      next(err);
      return;
    }

    next();
  },

  // `require_self` causes the body of the current file to be
  // inserted before any subsequent `require` or `include`
  // directives. Useful in CSS files, where it's common for the
  // index file to contain global styles that need to be defined
  // before other dependencies are loaded.
  //
  //     /*= require "reset"
  //      *= require_self
  //      *= require_tree .
  //      */
  //
  require_self: function (self, args, next) {
    if (self.__hasWrittenBody__) {
      next(new Error("require_self can only be called once per source file"));
      return;
    }

    try {
      self.context.requireAsset(self.pathname);
    } catch (err) {
      next(err);
      return;
    }

    self.processSource(function (err) {
      if (err) {
        next(err);
        return;
      }

      prop(self, '__hasWrittenBody__', true);
      self.includedPathnames = [];

      next();
    });
  },

  // The `include` directive works similar to `require` but
  // inserts the contents of the dependency even if it already
  // has been required.
  //
  //     //= include "header"
  //
  include: function (self, args, next) {
    try {
      var pathname = self.context.resolve(args[0]);
      self.context.dependOnAsset(pathname);
      self.includedPathnames.push(pathname);
    } catch (err) {
      next(err);
      return;
    }

    next();
  },

  // `require_directory` requires all the files inside a single
  // directory. It's similar to `path/*` since it does not follow
  // nested directories.
  //
  //     //= require_directory "./javascripts"
  //
  require_directory: function (self, args, next) {
    var root, pathname = args[0] || '.', stat;

    if (isAbsolute(pathname)) {
      next(new Error("require_directory argument must be a relative path"));
      return;
    }

    try {
      root = path.resolve(path.dirname(self.pathname), pathname);
      stat = self.stat(root);
    } catch (err) {
      next(err);
      return;
    }

    if (!stat || !stat.isDirectory()) {
      next(new Error("require_directory argument must be a directory"));
      return;
    }

    try {
      self.context.dependOn(root);
      _.each(self.entries(root), function (pathname) {
        pathname = path.join(root, pathname);

        if (self.file === pathname) {
          return;
        } else if (self.context.isAssetRequirable(pathname)) {
          self.context.requireAsset(pathname);
        }
      });
    } catch (err) {
      next(err);
      return;
    }

    next();
  },

  // `require_tree` requires all the nested files in a directory.
  // Its glob equivalent is `path/**/*`.
  //
  //     //= require_tree "./public"
  //
  require_tree: function (self, args, next) {
    var root, pathname = args[0] || '.', stat;

    if (isAbsolute(pathname)) {
      next(new Error("require_tree argument must be a relative path"));
      return;
    }

    try {
      root = path.resolve(path.dirname(self.pathname), pathname);
      stat = self.stat(root);
    } catch (err) {
      next(err);
      return;
    }

    if (!stat || !stat.isDirectory()) {
      next(new Error("require_tree argument must be a directory"));
      return;
    }

    try {
      self.context.dependOn(root);
      self.eachEntry(root, function (pathname) {
        if (self.file === pathname) {
          return;
        } else if (self.stat(pathname).isDirectory()) {
          self.context.dependOn(pathname);
        } else if (self.context.isAssetRequirable(pathname)) {
          self.context.requireAsset(pathname);
        }
      });
    } catch (err) {
      next(err);
      return;
    }

    next();
  },

  // Allows you to state a dependency on a file without
  // including it.
  //
  // This is used for caching purposes. Any changes made to
  // the dependency file will invalidate the cache of the
  // source file.
  //
  // This is useful if you are using ERB and File.read to pull
  // in contents from another file.
  //
  //     //= depend_on "foo.png"
  //
  depend_on: function (self, args, next) {
    try {
      self.context.dependOn(args[0]);
    } catch (err) {
      next(err);
      return;
    }

    next();
  },

  // Allows you to state a dependency on an asset without including
  // it.
  //
  // This is used for caching purposes. Any changes that would
  // invalid the asset dependency will invalidate the cache our the
  // source file.
  //
  // Unlike `depend_on`, the path must be a requirable asset.
  //
  //     //= depend_on_asset "bar.js"
  //
  depend_on_asset: function (self, args, next) {
    try {
      self.context.dependOnAsset(args[0]);
    } catch (err) {
      next(err);
      return;
    }

    next();
  },

  // Allows dependency to be excluded from the asset bundle.
  //
  // The `path` must be a valid asset and may or may not already
  // be part of the bundle. Once stubbed, it is blacklisted and
  // can't be brought back by any other `require`.
  //
  //     //= stub "jquery"
  //
  stub: function (self, args, next) {
    try {
      self.context.stubAsset(args[0]);
    } catch (err) {
      next(err);
      return;
    }

    next();
  }
};

////////////////////////////////////////////////////////////////////////////////

// Class constructor
var DirectiveProcessor = module.exports = function DirectiveProcessor() {
  Template.apply(this, arguments);
};

require('util').inherits(DirectiveProcessor, Template);

// Run processor
DirectiveProcessor.prototype.evaluate = function (context, locals, callback) {
  var self    = this,
      header  = (HEADER_PATTERN.exec(this.data) || []).shift() || '';

  // drop trailing spaces and line breaks
  header = header.trimRight();

  prop(this, 'pathname',            this.file);
  prop(this, 'header',              header);
  prop(this, 'body',                this.data.substr(header.length) + "\n");
  prop(this, 'includedPathnames',   [], {writable: true});

  prop(this, 'context',             context);
  prop(this, 'result',              '', {writable: true});

  async.series([
    function (next) { self.processDirectives(next); },
    function (next) { self.processSource(next); }
  ],
  // all done. fire callback
  function (err) {
    callback(err, self.result);
  });
};

/**
 *  DirectiveProcessor#processDirectives(callback) -> Void
 *
 *  Executes handlers for found directives.
 *
 *  ##### See Also:
 *
 *  - [[DirectiveProcessor#directives]]
 **/
DirectiveProcessor.prototype.processDirectives = function (callback) {
  var self = this;

  // Execute handler for each found directive
  async.mapSeries(this.directives, function (arr, next) {
    // arr = [
    //         10,         # 0: LINE
    //         'require',  # 1: DIRECTIVE
    //         ['foobar']  # 2: [ARGUMENTS]
    //       ]
    DIRECTIVE_HANDLERS[arr[1]](self, arr[2], function (err) {
      next(err);
    });
  }, callback);
};

DirectiveProcessor.prototype.processSource = function (callback) {
  var self = this;

  // if our own body was not yet appended, and there are header comments,
  // prepend these coments first.
  if (!self.__hasWrittenBody__ && 0 < self.processedHeader.length) {
    self.result += self.processedHeader + "\n";
  }

  // process and append body of each path that should be included
  async.forEachSeries(self.includedPathnames, function (pathname, next) {
    self.context.evaluate(pathname, {}, function (err, result) {
      self.result += result;
      next(err);
    });
  }, function (err) {
    // append own body of source only, if it was not yet written
    // (with `require_self` directive).
    if (!self.__hasWrittenBody__) {
      self.result += self.body;
    }

    // all done
    callback(err);
  });
};

// Tells whenever given line is directive or not by
// comparing found directives line indexes with `lineno`
function is_directive(directives, lineno) {
  return _.any(directives, function (arr) { return arr[0] === lineno; });
}

/**
 *  DirectiveProcessor#processedHeader -> String
 *
 *  Returns the header String with any directives stripped.
 **/
getter(DirectiveProcessor.prototype, 'processedHeader', function () {
  var header;

  if (!this.__processedHeader__) {
    header = get_lines(this.header).map(function (line, index) {
      return is_directive(this.directives, index + 1) ? "\n" : line;
    }, this).join("\n").trimLeft().trimRight();

    prop(this, '__processedHeader__', header);
  }

  return this.__processedHeader__;
});

/**
 *  DirectiveProcessor#processedSource -> String
 *
 *  Returns the source String with any directives stripped.
 **/
getter(DirectiveProcessor.prototype, 'processedSource', function () {
  if (!this.__processedSource__) {
    this.__processedSource__ = this.processedHeader + this.body;
  }

  return this.__processedSource__;
});

/**
 *  DirectiveProcessor#directives -> Array
 *
 *  Returns an Array of directive structures. Each structure
 *  is an Array with the line number as the first element, the
 *  directive name as the second element, third is an array of
 *  arguments.
 *
 *      [[1, "require", ["foo"]], [2, "require", ["bar"]]]
 **/
getter(DirectiveProcessor.prototype, 'directives', function () {
  if (!this.__directives__) {
    prop(this, '__directives__', []);

    get_lines(this.header).forEach(function (line, index) {
      var matches = DIRECTIVE_PATTERN.exec(line), name, args;

      if (matches && matches[1]) {
        args = shellwords(matches[1]);
        name = args.shift();

        if (_.isFunction(DIRECTIVE_HANDLERS[name])) {
          this.__directives__.push([index + 1, name, args]);
        }
      }
    }, this);
  }

  return this.__directives__;
});

DirectiveProcessor.prototype.stat = function (pathname) {
  return this.context.environment.stat(pathname);
};

DirectiveProcessor.prototype.entries = function (pathname) {
  return this.context.environment.entries(pathname);
};

DirectiveProcessor.prototype.eachEntry = function (path, func) {
  return this.context.environment.eachEntry(path, func);
};
<commit_msg>Cleanup directive processor regexp<commit_after>/**
 *  class DirectiveProcessor
 *
 *  The `DirectiveProcessor` is responsible for parsing and evaluating
 *  directive comments in a source file.
 *
 *  A directive comment starts with a comment prefix, followed by an "=",
 *  then the directive name, then any arguments.
 *
 *  - **JavaScript one-line comments:**       `//= require "foo"
 *  - **CoffeeScript one-line comments:**     `#= require "baz"
 *  - **JavaScript and CSS block comments:**  `*= require "bar"
 *
 *  This behavior can be disabled with:
 *
 *      environment.unregisterPreProcessor('text/css', DirectiveProcessor);
 *      environment.unregisterPreProcessor('application/javascript', DirectiveProcessor);
 *
 *
 *  ##### SUBCLASS OF
 *
 *  [[Template]]
 **/

'use strict';

// stdlib
var path = require('path');

// 3rd-party
var _           = require('underscore');
var async       = require('async');
var shellwords  = require('shellwords').split;

// internal
var Template    = require('../template');
var prop        = require('../common').prop;
var getter      = require('../common').getter;
var isAbsolute  = require('../common').isAbsolute;
var isRelative  = require('../common').isRelative;

////////////////////////////////////////////////////////////////////////////////

// Returns an Array of lines.
// Originl idea of cross-platform line splitting taken from Sugr.JS:
// https://github.com/andrewplummer/Sugar/blob/f6d1c2e9/lib/string.js#L323
function get_lines(str) {
  return String(str || '').match(/^.*$/gm);
}

// Directives will only be picked up if they are in the header
// of the source file. C style (/* */), JavaScript (//), and
// Ruby (#) comments are supported.
//
// Directives in comments after the first non-whitespace line
// of code will not be processed.
var HEADER_PATTERN = new RegExp(
  '^(?:\\s*' +
    '(' +
      '(?:\/[*](?:\\s*|.+?)*?[*]\/)' + '|' +
      '(?:\/\/.*\n?)+' + '|' +
      '(?:#.*\n?)+' +
    ')*' +
  ')*', 'm');

// Directives are denoted by a `=` followed by the name, then
// argument list.
//
// A few different styles are allowed:
//
//     // =require foo
//     //= require foo
//     //= require "foo"
//
var DIRECTIVE_PATTERN = new RegExp('^\\W*=\\s*(\\w+.*?)(\\*\\/)?$');

// Real directive processors
var DIRECTIVE_HANDLERS = {
  // The `require` directive functions similar to Ruby's `require`.
  // It provides a way to declare a dependency on a file in your path
  // and ensures its only loaded once before the source file.
  //
  // `require` works with files in the environment path:
  //
  //     //= require "foo.js"
  //
  // Extensions are optional. If your source file is ".js", it
  // assumes you are requiring another ".js".
  //
  //     //= require "foo"
  //
  // Relative paths work too. Use a leading `./` to denote a relative
  // path:
  //
  //     //= require "./bar"
  //
  require: function (self, args, next) {
    var pathname = isRelative(args[0]) ? args[0] : ('./' + args[0]);

    try {
      self.context.requireAsset(pathname);
    } catch (err) {
      next(err);
      return;
    }

    next();
  },

  // `require_self` causes the body of the current file to be
  // inserted before any subsequent `require` or `include`
  // directives. Useful in CSS files, where it's common for the
  // index file to contain global styles that need to be defined
  // before other dependencies are loaded.
  //
  //     /*= require "reset"
  //      *= require_self
  //      *= require_tree .
  //      */
  //
  require_self: function (self, args, next) {
    if (self.__hasWrittenBody__) {
      next(new Error("require_self can only be called once per source file"));
      return;
    }

    try {
      self.context.requireAsset(self.pathname);
    } catch (err) {
      next(err);
      return;
    }

    self.processSource(function (err) {
      if (err) {
        next(err);
        return;
      }

      prop(self, '__hasWrittenBody__', true);
      self.includedPathnames = [];

      next();
    });
  },

  // The `include` directive works similar to `require` but
  // inserts the contents of the dependency even if it already
  // has been required.
  //
  //     //= include "header"
  //
  include: function (self, args, next) {
    try {
      var pathname = self.context.resolve(args[0]);
      self.context.dependOnAsset(pathname);
      self.includedPathnames.push(pathname);
    } catch (err) {
      next(err);
      return;
    }

    next();
  },

  // `require_directory` requires all the files inside a single
  // directory. It's similar to `path/*` since it does not follow
  // nested directories.
  //
  //     //= require_directory "./javascripts"
  //
  require_directory: function (self, args, next) {
    var root, pathname = args[0] || '.', stat;

    if (isAbsolute(pathname)) {
      next(new Error("require_directory argument must be a relative path"));
      return;
    }

    try {
      root = path.resolve(path.dirname(self.pathname), pathname);
      stat = self.stat(root);
    } catch (err) {
      next(err);
      return;
    }

    if (!stat || !stat.isDirectory()) {
      next(new Error("require_directory argument must be a directory"));
      return;
    }

    try {
      self.context.dependOn(root);
      _.each(self.entries(root), function (pathname) {
        pathname = path.join(root, pathname);

        if (self.file === pathname) {
          return;
        } else if (self.context.isAssetRequirable(pathname)) {
          self.context.requireAsset(pathname);
        }
      });
    } catch (err) {
      next(err);
      return;
    }

    next();
  },

  // `require_tree` requires all the nested files in a directory.
  // Its glob equivalent is `path/**/*`.
  //
  //     //= require_tree "./public"
  //
  require_tree: function (self, args, next) {
    var root, pathname = args[0] || '.', stat;

    if (isAbsolute(pathname)) {
      next(new Error("require_tree argument must be a relative path"));
      return;
    }

    try {
      root = path.resolve(path.dirname(self.pathname), pathname);
      stat = self.stat(root);
    } catch (err) {
      next(err);
      return;
    }

    if (!stat || !stat.isDirectory()) {
      next(new Error("require_tree argument must be a directory"));
      return;
    }

    try {
      self.context.dependOn(root);
      self.eachEntry(root, function (pathname) {
        if (self.file === pathname) {
          return;
        } else if (self.stat(pathname).isDirectory()) {
          self.context.dependOn(pathname);
        } else if (self.context.isAssetRequirable(pathname)) {
          self.context.requireAsset(pathname);
        }
      });
    } catch (err) {
      next(err);
      return;
    }

    next();
  },

  // Allows you to state a dependency on a file without
  // including it.
  //
  // This is used for caching purposes. Any changes made to
  // the dependency file will invalidate the cache of the
  // source file.
  //
  // This is useful if you are using ERB and File.read to pull
  // in contents from another file.
  //
  //     //= depend_on "foo.png"
  //
  depend_on: function (self, args, next) {
    try {
      self.context.dependOn(args[0]);
    } catch (err) {
      next(err);
      return;
    }

    next();
  },

  // Allows you to state a dependency on an asset without including
  // it.
  //
  // This is used for caching purposes. Any changes that would
  // invalid the asset dependency will invalidate the cache our the
  // source file.
  //
  // Unlike `depend_on`, the path must be a requirable asset.
  //
  //     //= depend_on_asset "bar.js"
  //
  depend_on_asset: function (self, args, next) {
    try {
      self.context.dependOnAsset(args[0]);
    } catch (err) {
      next(err);
      return;
    }

    next();
  },

  // Allows dependency to be excluded from the asset bundle.
  //
  // The `path` must be a valid asset and may or may not already
  // be part of the bundle. Once stubbed, it is blacklisted and
  // can't be brought back by any other `require`.
  //
  //     //= stub "jquery"
  //
  stub: function (self, args, next) {
    try {
      self.context.stubAsset(args[0]);
    } catch (err) {
      next(err);
      return;
    }

    next();
  }
};

////////////////////////////////////////////////////////////////////////////////

// Class constructor
var DirectiveProcessor = module.exports = function DirectiveProcessor() {
  Template.apply(this, arguments);
};

require('util').inherits(DirectiveProcessor, Template);

// Run processor
DirectiveProcessor.prototype.evaluate = function (context, locals, callback) {
  var self    = this,
      header  = (HEADER_PATTERN.exec(this.data) || []).shift() || '';

  // drop trailing spaces and line breaks
  header = header.trimRight();

  prop(this, 'pathname',            this.file);
  prop(this, 'header',              header);
  prop(this, 'body',                this.data.substr(header.length) + "\n");
  prop(this, 'includedPathnames',   [], {writable: true});

  prop(this, 'context',             context);
  prop(this, 'result',              '', {writable: true});

  async.series([
    function (next) { self.processDirectives(next); },
    function (next) { self.processSource(next); }
  ],
  // all done. fire callback
  function (err) {
    callback(err, self.result);
  });
};

/**
 *  DirectiveProcessor#processDirectives(callback) -> Void
 *
 *  Executes handlers for found directives.
 *
 *  ##### See Also:
 *
 *  - [[DirectiveProcessor#directives]]
 **/
DirectiveProcessor.prototype.processDirectives = function (callback) {
  var self = this;

  // Execute handler for each found directive
  async.mapSeries(this.directives, function (arr, next) {
    // arr = [
    //         10,         # 0: LINE
    //         'require',  # 1: DIRECTIVE
    //         ['foobar']  # 2: [ARGUMENTS]
    //       ]
    DIRECTIVE_HANDLERS[arr[1]](self, arr[2], function (err) {
      next(err);
    });
  }, callback);
};

DirectiveProcessor.prototype.processSource = function (callback) {
  var self = this;

  // if our own body was not yet appended, and there are header comments,
  // prepend these coments first.
  if (!self.__hasWrittenBody__ && 0 < self.processedHeader.length) {
    self.result += self.processedHeader + "\n";
  }

  // process and append body of each path that should be included
  async.forEachSeries(self.includedPathnames, function (pathname, next) {
    self.context.evaluate(pathname, {}, function (err, result) {
      self.result += result;
      next(err);
    });
  }, function (err) {
    // append own body of source only, if it was not yet written
    // (with `require_self` directive).
    if (!self.__hasWrittenBody__) {
      self.result += self.body;
    }

    // all done
    callback(err);
  });
};

// Tells whenever given line is directive or not by
// comparing found directives line indexes with `lineno`
function is_directive(directives, lineno) {
  return _.any(directives, function (arr) { return arr[0] === lineno; });
}

/**
 *  DirectiveProcessor#processedHeader -> String
 *
 *  Returns the header String with any directives stripped.
 **/
getter(DirectiveProcessor.prototype, 'processedHeader', function () {
  var header;

  if (!this.__processedHeader__) {
    header = get_lines(this.header).map(function (line, index) {
      return is_directive(this.directives, index + 1) ? "\n" : line;
    }, this).join("\n").trimLeft().trimRight();

    prop(this, '__processedHeader__', header);
  }

  return this.__processedHeader__;
});

/**
 *  DirectiveProcessor#processedSource -> String
 *
 *  Returns the source String with any directives stripped.
 **/
getter(DirectiveProcessor.prototype, 'processedSource', function () {
  if (!this.__processedSource__) {
    this.__processedSource__ = this.processedHeader + this.body;
  }

  return this.__processedSource__;
});

/**
 *  DirectiveProcessor#directives -> Array
 *
 *  Returns an Array of directive structures. Each structure
 *  is an Array with the line number as the first element, the
 *  directive name as the second element, third is an array of
 *  arguments.
 *
 *      [[1, "require", ["foo"]], [2, "require", ["bar"]]]
 **/
getter(DirectiveProcessor.prototype, 'directives', function () {
  if (!this.__directives__) {
    prop(this, '__directives__', []);

    get_lines(this.header).forEach(function (line, index) {
      var matches = DIRECTIVE_PATTERN.exec(line), name, args;

      if (matches && matches[1]) {
        args = shellwords(matches[1]);
        name = args.shift();

        if (_.isFunction(DIRECTIVE_HANDLERS[name])) {
          this.__directives__.push([index + 1, name, args]);
        }
      }
    }, this);
  }

  return this.__directives__;
});

DirectiveProcessor.prototype.stat = function (pathname) {
  return this.context.environment.stat(pathname);
};

DirectiveProcessor.prototype.entries = function (pathname) {
  return this.context.environment.entries(pathname);
};

DirectiveProcessor.prototype.eachEntry = function (path, func) {
  return this.context.environment.eachEntry(path, func);
};
<|endoftext|>