<|start_filename|>package.json<|end_filename|>
{
  "name": "invig",
  "version": "0.0.22",
  "description": "Take a legacy JS or CoffeeScript codebase and automatically convert it to ES6 & standardjs.com",
  "main": "lib/cli.js",
  "bin": "lib/cli.js",
  "preferGlobal": true,
  "jest": {
    "verbose": true,
    "roots": [
      "lib/"
    ]
  },
  "lanyon": {
    "projectDir": "website",
    "prebuild": "./_scripts/inject.sh",
    "contentScandir": "../",
    "contentIgnore": [
      "website/*.md"
    ]
  },
  "scripts": {
    "build:watch": "babel src --watch --source-maps --out-dir lib",
    "build": "babel src --source-maps --out-dir lib",
    "fix": "eslint . --fix",
    "jest:update": "env DEBUG=invig:* NODE_ENV=test jest --updateSnapshot --forceExit",
    "jest": "env DEBUG=invig:* NODE_ENV=test jest --forceExit",
    "demo": "scripts/demo1.sh",
    "lint": "eslint .",
    "npublish": "npm publish",
    "playground": "cp fixture/package.json fixture/hack-sitemap.coffee /tmp && env SCROLEX_INTERVAL=10000 node src/cli.js --src /tmp/hack-sitemap.coffee; cat /tmp/hack-sitemap.js",
    "push": "git push && git push --tags",
    "release:major": "env SEMANTIC=major npm run release",
    "release:minor": "env SEMANTIC=minor npm run release",
    "release:patch": "env SEMANTIC=patch npm run release",
    "release": "npm version ${SEMANTIC:-patch} -m \"Release %s\" && npm-run-all build push npublish",
    "test:update": "npm run jest:update",
    "test": "npm run jest",
    "web:build:production": "LANYON_ENV=production lanyon build",
    "web:build": "lanyon build",
    "web:deploy": "lanyon deploy",
    "web:encrypt": "lanyon encrypt",
    "web:install": "lanyon install",
    "web:serve:production": "LANYON_ENV=production lanyon serve",
    "web:start": "lanyon start",
    "upgrade:modules": "npm-check-updates -u --semverLevel major && yarn && npm run test:update && echo 'Inspect Git diff now plz'"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/kvz/invig.git"
  },
  "author": "",
  "license": "MIT",
  "files": [
    "lib",
    ".eslintignore",
    ".eslintrc",
    ".babelrc"
  ],
  "bugs": {
    "url": "https://github.com/kvz/invig/issues"
  },
  "dependencies": {
    "babel-cli": "6.26.0",
    "babel-core": "6.26.0",
    "babel-eslint": "8.0.2",
    "babel-jest": "21.3.0-beta.8",
    "babel-plugin-add-module-exports": "0.2.1",
    "babel-plugin-es6-promise": "1.1.1",
    "babel-plugin-syntax-async-functions": "6.13.0",
    "babel-plugin-transform-async-to-generator": "6.24.1",
    "babel-plugin-transform-object-assign": "6.22.0",
    "babel-polyfill": "6.26.0",
    "babel-preset-es2015": "6.24.1",
    "commander": "2.11.0",
    "decaffeinate": "3.3.13",
    "depurar": "0.3.0",
    "es6-promise": "4.1.1",
    "eslint": "4.11.0",
    "eslint-config-standard": "10.2.1",
    "eslint-plugin-import": "2.8.0",
    "eslint-plugin-node": "5.2.1",
    "eslint-plugin-promise": "3.6.0",
    "eslint-plugin-react": "7.4.0",
    "eslint-plugin-standard": "3.0.1",
    "fs-extra": "4.0.2",
    "get-stdin": "5.0.1",
    "globby": "6.1.0",
    "lanyon": "0.0.106",
    "lebab": "2.7.7",
    "npm-check-updates": "2.13.0",
    "os-tmpdir": "1.0.2",
    "pkg-up": "2.0.0",
    "prettier": "1.8.2",
    "prettier-eslint": "8.2.1",
    "scrolex": "0.0.27",
    "shelljs": "0.7.8",
    "strip-ansi": "4.0.0",
    "undefined": "0.1.0",
    "untildify": "3.0.2"
  },
  "devDependencies": {
    "cli-spinners": "1.1.0",
    "fakefile": "0.0.9",
    "jest": "21.3.0-beta.8",
    "npm-run-all": "4.1.2",
    "regenerator-runtime": "0.11.0"
  },
  "homepage": "https://github.com/kvz/invig#readme"
}


<|start_filename|>src/removeVariance.js<|end_filename|>
import stripAnsi from 'strip-ansi'
import path from 'path'
import { dots10 as cliSpinner } from 'cli-spinners'

const removeVariance = input => {
  if (input && input.message) {
    input.message = removeVariance(input.message)
  }
  if (`${input}` !== input) {
    for (let k in input) {
      input[k] = removeVariance(input[k])
    }
    return input
  }

  const map = {
    SCROLEX_ROOT: path.resolve(path.join(__dirname, '..')),
    PWD         : process.cwd(),
    HOME        : process.env.HOME,
    USER        : process.env.USER,
  }

  for (let key in map) {
    let val = map[key]
    while (input.includes(val)) {
      input = input.replace(val, `#{${key}}`)
    }
  }

  input = stripAnsi(input)

  input = input.replace(/^.*No license field.*\n/gm, '')
  input = input.replace(/^.*Already up-to-date.*\n/gm, '')
  input = input.replace(/^.*Building fresh packages.*\n/gm, '')
  input = input.replace(/^.*Fetching package.*\n/gm, '')
  input = input.replace(/^.*fsevents.*Excluding it from installation.*\n/gm, '')
  input = input.replace(/^.*fsevents.*incompatible with this module.*\n/gm, '')
  input = input.replace(/^.*Linking dependencies.*\n/gm, '')
  input = input.replace(/^.*peer dependency "es6-promise.*\n/gm, '')
  input = input.replace(/^.*Resolving packages.*\n/gm, '')
  input = input.replace(/^.*There appears to be trouble with your network connection.*\n/gm, '')
  input = input.replace(/Done in \d+\.\d+s/g, 'Done in X.Xs')
  input = input.replace(/\d+ms/g, 'XXms')
  input = input.replace(/yarn install v\d+\.\d+\.\d+/g, 'yarn install vX.X.X')

  // @todo: Remove this hack when scrolex no longer adds trailing spinner frames:
  cliSpinner.frames.forEach(frame => {
    while (input.includes(frame)) {
      // console.log({input, frame})
      input = input.replace(frame, '---spinnerframe---')
    }
  })

  return input
}

export default removeVariance


<|start_filename|>src/Invig.js<|end_filename|>
import scrolex from 'scrolex'
import globby from 'globby'
import path from 'path'
import pkgUp from 'pkg-up'
import lebab from 'lebab'
import fs from 'fs-extra'
import format from 'prettier-eslint'
import eslintConfig from '../.eslintrc'
import depurar from 'depurar'
import ncu from 'npm-check-updates'

const debug = depurar('invig')

class Invig {
  constructor (opts = {}) {
    this.opts = opts
  }

  async initProject () {
    try {
      const projectPackage = await fs.readJson(this._projectPackagePath, {
        throws: false,
      })
      const projectRoot = path.dirname(this._projectPackagePath)
      const projectRootRel = path.relative(process.cwd(), projectRoot) || '.'
      const invigRoot = `${__dirname}/..`
      // const invigRootRel = path.relative(process.cwd(), invigRoot)
      const invigPackage = await fs.readJson(`${invigRoot}/package.json`, { throws: false })
      let npmInstallNeeded = []

      scrolex.stick('Adding npm task project config', {
        components: `invig>${projectRootRel}`,
      })
      if (this.opts.dryrun === false) {
        if (!projectPackage.scripts) {
          projectPackage.scripts = {}
        }
        if (!projectPackage.scripts.lint) {
          projectPackage.scripts.lint = 'eslint .'
        }
        if (!projectPackage.scripts.fix) {
          projectPackage.scripts.fix = 'eslint . --fix'
        }
        if (!projectPackage.scripts.build) {
          projectPackage.scripts.build = 'babel src --source-maps --out-dir lib'
          if (!projectPackage.scripts['build:watch']) {
            projectPackage.scripts['build:watch'] = 'babel src --watch --source-maps --out-dir lib'
          }
        }
      }

      scrolex.stick('Adding dependencies task project config', {
        components: `invig>${projectRootRel}`,
      })
      if (this.opts.dryrun === false) {
        if (!projectPackage.dependencies) {
          projectPackage.dependencies = {}
        }
        if (!projectPackage.devDependencies) {
          projectPackage.devDependencies = {}
        }

        Object.keys(invigPackage.dependencies).forEach(name => {
          if (name.match(/^(babel|eslint|es6-promise)/)) {
            if (projectPackage.devDependencies[name] !== invigPackage.dependencies[name]) {
              projectPackage.devDependencies[name] = invigPackage.dependencies[name]
              npmInstallNeeded.push(`Add ${name} to devDependencies`)
            }
          }
        })

        const removeDeps = ['coffee-script', 'coffeelint']
        removeDeps.forEach(name => {
          if (projectPackage.dependencies[name]) {
            delete projectPackage.dependencies[name]
            npmInstallNeeded.push(`Remove ${name} from dependencies`)
          }
          if (projectPackage.devDependencies[name]) {
            delete projectPackage.devDependencies[name]
            npmInstallNeeded.push(`Remove ${name} from devDependencies`)
          }
        })
      }

      scrolex.stick('Writing eslint project config', {
        components: `invig>${projectRootRel}`,
      })
      if (this.opts.dryrun === false) {
        this.copySyncNoOverwrite(`${invigRoot}/.eslintrc.js`, `${projectRoot}/.eslintrc.js`)
      }

      scrolex.stick('Writing babel project config', {
        components: `invig>${projectRootRel}`,
      })
      if (this.opts.dryrun === false) {
        this.copySyncNoOverwrite(`${invigRoot}/.babelrc`, `${projectRoot}/.babelrc`)
      }

      scrolex.stick('Writing eslint ignores', {
        components: `invig>${projectRootRel}`,
      })
      if (this.opts.dryrun === false) {
        this.copySyncNoOverwrite(`${invigRoot}/.eslintignore`, `${projectRoot}/.eslintignore`)
      }

      scrolex.stick('Writing back project config ', {
        components: `invig>${projectRootRel}`,
      })
      fs.writeFileSync(this._projectPackagePath, JSON.stringify(projectPackage, null, 2), 'utf-8')

      if (npmInstallNeeded.length > 0) {
        scrolex.stick(`Running npm install to accomodate these changes: ${npmInstallNeeded.join('. ')}`, {
          components: `invig>${projectRootRel}`,
        })
        const cmd = `yarn || npm install`
        scrolex.exe(cmd, { cwd: this._projectDir, components: `invig>${projectRootRel}` }, err => {
          if (err) throw err
        })
      } else {
        return
      }
    } catch (err) {
      throw err
    }
  }

  toJs (srcPath) {
    return new Promise((resolve, reject) => {
      scrolex.stick('Converting from coffee to js', {
        components: `invig>${srcPath}`,
      })
      const cmd = `${this.opts.npmBinDir}/decaffeinate \
      --no-array-includes \
      --loose-includes \
      --loose-for-of \
      --allow-invalid-constructors \
      --loose-default-params ${srcPath} && \
      rm -f ${srcPath}`
      scrolex.exe(
        cmd,
        {
          cwd       : this._projectDir,
          components: `invig>${path.relative(process.cwd(), srcPath)}`,
        },
        (err, result) => {
          if (err) reject(err)
          resolve(result)
        }
      )
    })
  }

  async transform (srcPath, args) {
    try {
      srcPath = srcPath.replace(/\.coffee$/, '.js')
      const sourceCode = await fs.readFile(srcPath, 'utf8')
      const { code } = lebab.transform(sourceCode, args)
      if (!this.opts.dryrun) {
        await fs.outputFile(srcPath, code)
      }
    } catch (err) {
      throw err
    }
  }

  toEs6 (srcPath) {
    return new Promise((resolve, reject) => {
      scrolex.stick('Upgrading to Es6', { components: `invig>${srcPath}` })
      const safe = [
        'arrow',
        'for-of',
        'for-each',
        'arg-rest',
        'arg-spread',
        'obj-method',
        'obj-shorthand',
        'no-strict',
        'multi-var',
      ]
      const unsafe = ['let', 'class', 'template', 'default-param', 'destruct-param']
      const list = [].concat(safe, unsafe)
      this.transform(srcPath, list).then(resolve).catch(reject)
    })
  }

  toEs7 (srcPath) {
    return new Promise((resolve, reject) => {
      scrolex.stick('Upgrading to Es7', { components: `invig>${srcPath}` })
      const safe = [
        'arrow',
        'for-of',
        'for-each',
        'arg-rest',
        'arg-spread',
        'obj-method',
        'obj-shorthand',
        'no-strict',
        'exponent',
        'multi-var',
      ]
      const unsafe = ['let', 'class', 'commonjs', 'template', 'default-param', 'destruct-param', 'includes']
      const list = [].concat(safe, unsafe)
      this.transform(srcPath, list).then(resolve).catch(reject)
    })
  }

  toPrettier (srcPath) {
    return new Promise(async (resolve, reject) => {
      scrolex.stick('Making Pretty', { components: `invig>${srcPath}` })
      srcPath = srcPath.replace(/\.coffee$/, '.js')
      const sourceCode = await fs.readFile(srcPath, 'utf8')
      const options = {
        text           : sourceCode,
        eslintConfig,
        prettierOptions: {
          bracketSpacing: true,
        },
        fallbackPrettierOptions: {
          singleQuote: true,
          printWidth : 120,
        },
      }
      const code = format(options)
      if (this.opts.dryrun) resolve()
      fs.outputFile(srcPath, code).then(resolve).catch(reject)
    })
  }

  async convertFile (srcPath) {
    const extension = path.extname(srcPath).toLowerCase()
    try {
      if (extension === '.coffee') {
        await this.toJs.bind(this)(srcPath)
      }
      if (this.opts.es7) {
        await this.toEs7.bind(this)(srcPath)
      } else {
        await this.toEs6.bind(this)(srcPath)
      }
      await this.toPrettier.bind(this)(srcPath)
    } catch (err) {
      throw err
    }
  }

  findFiles () {
    return new Promise((resolve, reject) => {
      let files = []
      let stat = {}
      try {
        stat = fs.lstatSync(this.opts.src)
      } catch (e) {
        stat = false
      }
      // let relative
      if (stat && stat.isFile()) {
        // File
        const resolve = path.resolve(this.opts.src)
        files = [resolve]
      } else if (stat && stat.isDirectory()) {
        // Directory
        const resolve = path.resolve(this.opts.src)
        files = globby.sync([`${resolve}/**/*.coffee`, `${resolve}/**/*.js`, `!${resolve}/node_modules/**`])
      } else {
        // Pattern
        files = globby.sync(this.opts.src)
      }
      if (!files || files.length === 0) {
        reject(new Error(`Source argument: "${this.opts.src}" returned no input files to work on.`))
      }
      resolve(files)
    })
  }

  copySyncNoOverwrite (src, dst) {
    if (!fs.existsSync(dst)) {
      fs.writeFileSync(dst, fs.readFileSync(src, 'utf-8'), 'utf-8')
    }
  }

  findProject (files) {
    return new Promise((resolve, reject) => {
      this._projectPackagePath = pkgUp.sync(path.dirname(files[0]))
      if (!this._projectPackagePath) {
        reject(new Error(`No package.json found, unable to establish project root. `))
      }
      this._projectDir = path.dirname(this._projectPackagePath)
      resolve()
    })
  }

  processFiles (files) {
    return new Promise((resolve, reject) => {
      Promise.all(files.map(this.convertFile.bind(this))).then(resolve).catch(reject)
    })
  }

  upgradeDeps () {
    return new Promise((resolve, reject) => {
      if (this.opts.check) {
        scrolex.stick('Checking installed packages', {
          components: `invig>package.json`,
        })
        ncu
          .run({
            packageFile: 'package.json',
            upgradeAll : true,
          })
          .then(upgraded => {
            let upgrades = ''
            Object.entries(upgraded).forEach(([key, val]) => {
              upgrades += ` ${key}@${val}`
            })
            const cmd = `yarn upgrade ${upgrades} || npm upgrade ${upgrades}`
            scrolex.exe(cmd, { cwd: this._projectDir, components: 'invig>package.json' }, err => {
              if (err) reject(err)
              resolve()
            })
          })
          .catch(reject)
      } else {
        resolve()
      }
    })
  }

  async runOnStdIn (stdin) {
    this._projectDir = `${__dirname}/..`
    const coffeePath = `${__dirname}/tmpFile.coffee`
    let jsPath = `${__dirname}/tmpFile.js`
    try {
      await fs.outputFile(coffeePath, stdin)
      scrolex.persistOpts({ mode: 'silent' })
      this.convertFile(jsPath).then(err => {
        debug(err)
        process.stdout.write(fs.readFileSync(jsPath, 'utf-8'))
        fs.unlinkSync(jsPath)
        fs.unlinkSync(coffeePath)
      })
    } catch (err) {
      throw err
    }
  }

  async runOnPattern () {
    try {
      const files = await this.findFiles.bind(this)()
      await this.findProject.bind(this)(files)
      await this.initProject.bind(this)()
      await this.processFiles.bind(this)(files)
      await this.upgradeDeps.bind(this)()
    } catch (err) {
      throw err
    }
  }
}

export default Invig


<|start_filename|>src/cli.test.js<|end_filename|>
import shelljs from 'shelljs'
import fs from 'fs'
import path from 'path'
import childProcess from 'child_process'
import globby from 'globby'
import removeVariance from './removeVariance'

const fixDir = `${__dirname}/../fixture`
const tmpDir = `${__dirname}/../fixture/tmp`

shelljs.rm('-fR', tmpDir)
shelljs.mkdir('-p', tmpDir)
shelljs.touch(`${tmpDir}/.empty`)

describe('Invig', () => {
  describe('cli', () => {
    test('invigorates an entire directory', () => {
      const files = globby.sync([`${fixDir}/*.coffee`, `${fixDir}/*.js`])
      files.forEach(src => {
        let base = path.basename(src)
        let dst = `${tmpDir}/${base}`

        shelljs.cp('-f', `${path.dirname(src)}/package.json`, `${path.dirname(dst)}/package.json`)
        shelljs.cp('-f', src, dst)
        const cmd = `env SCROLEX_MODE=passthru node ${__dirname}/cli.js --src "${dst}"`
        // console.log(cmd)
        const p = shelljs.exec(cmd)
        expect(removeVariance(p.stderr.trim())).toMatchSnapshot()
        expect(removeVariance(p.stdout.trim())).toMatchSnapshot()
        expect(p.code).toMatchSnapshot()
        expect(p.code).toBe(0)

        let result = fs.readFileSync(dst.replace(/\.coffee$/, '.js'), 'utf-8')
        expect(removeVariance(result.trim())).toMatchSnapshot()

        const pkg = fs.readFileSync(`${path.dirname(dst)}/package.json`, 'utf-8').trim()
        expect(pkg).toMatchSnapshot()
      })
    })
    test('invigorates via stdin', done => {
      const child = childProcess.spawn(process.argv[0], [`${__dirname}/cli.js`, `-qs`, `-`], {
        env: {
          SCROLEX_MODE: `passthru`,
        },
      })

      child.stdin.write(`console.log('hello')`)
      child.stdin.end()

      let buf = ''
      child.stdout.on('data', data => {
        buf += `${data}`
      })

      child.on('close', code => {
        expect(removeVariance(buf)).toMatchSnapshot()
        done()
      })
    })
  })
})


<|start_filename|>src/cli.js<|end_filename|>
#!/usr/bin/env node
import 'babel-core/register'
import 'babel-polyfill'
import program from 'commander'
import getStdin from 'get-stdin'
import path from 'path'
import Invig from './Invig'
import pkgUp from 'pkg-up'
import scrolex from 'scrolex'
import untildify from 'untildify'

const rootDir = path.dirname(pkgUp.sync(__filename))
const npmDir = `${rootDir}/node_modules`
const npmBinDir = `${npmDir}/.bin`

program
  .version(require('../package.json').version)
  .option('-s, --src <dir>', "Directory or file to convert. DESTRUCTIVE. MAKE SURE IT'S UNDER SOURCE CONTROL. ")
  .option('-b, --bail', 'Abort on the first error instead of continuing to port the next file')
  .option('-c, --check', 'When done, run dependency check to see if there are unused or unupdated ones')
  .option('-d, --dryrun', 'Wether to execute commands or just output them')
  .option('-q, --quiet', 'Hide any output')
  .option('-7, --es7', 'generate es7')
  .parse(process.argv)

if (!('src' in program)) {
  scrolex.failure('You should provide at least a --src <dir> argument')
  process.exit(1)
}

program.src = untildify(program.src)
program.init = !!program.init
program.dryrun = !!program.dryrun
program.bail = !!program.bail
program.quiet = !!program.quiet

scrolex.persistOpts({
  announce             : true,
  addCommandAsComponent: true,
  components           : 'invig',
  shell                : true,
  fatal                : program.bail,
  dryrun               : program.dryrun,
})
if (program.quiet === true) {
  scrolex.persistOpts({
    mode: 'silent',
  })
}

const invig = new Invig({
  src   : program.src,
  check : program.check,
  dryrun: program.dryrun,
  bail  : program.bail,
  init  : program.init,
  quiet : program.quiet,
  npmBinDir,
  es7   : program.es7,
})

if (program.src === '-') {
  getStdin().then(stdin => {
    invig
      .runOnStdIn(stdin)
      .catch(err => {
        scrolex.failure(`${err}`)
        process.exit(1)
      })
      .then(() => {
        scrolex.success(`Done`)
      })
  })
} else {
  invig
    .runOnPattern()
    .catch(err => {
      scrolex.failure(`${err}`)
      process.exit(1)
    })
    .then(() => {
      scrolex.success(`Done`)
    })
}


<|start_filename|>fixture/hack-sitemap.coffee<|end_filename|>
#!/usr/bin/env coffee
debug              = require("debug")("tlc:formats")
fs                 = require "fs"
fileSitemap        = "#{__dirname}/../_site/sitemap.xml"
sitemap            = fs.readFileSync(fileSitemap, "utf8")

baseUrl = "https://transloadit.com"

additions = [
  "/login/"
  "/accounts/forgot_pw/"
]

removals = [
  "/jekyll.lanyon_assets.yml"
  "/denied/"
  "/unpublished/"
]

xmlAppend = ""
for addition in additions
  debug "Processing '#{baseUrl}#{addition}'"
  item      = ""
  item      = "#{item}<url>\n"
  item      = "#{item}<loc>#{baseUrl}#{addition}</loc>\n"
  item      = "#{item}<lastmod>2016-08-17T10:09:28+02:00</lastmod>\n"
  item      = "#{item}</url>\n"
  xmlAppend = "#{xmlAppend}#{item}"

xmlAppend = "#{xmlAppend}</urlset>"

sitemap = sitemap.replace('</urlset>', xmlAppend)

for removal in removals
  pattern = new RegExp("<url>\\s*<loc>\\s*#{baseUrl}#{removal}[^<]*</loc>\\s*</url>\\s*", "gmi")
  sitemap = sitemap.replace(pattern, "")

debug "Writing #{fileSitemap}"
fs.writeFileSync(fileSitemap, sitemap, "utf8")
