<commit_before>path = require 'path'

async = require 'async'
_ = require 'underscore-plus'
optimist = require 'optimist'
CSON = require 'season'
semver = require 'npm/node_modules/semver'
temp = require 'temp'

config = require './apm'
Command = require './command'
fs = require './fs'
RebuildModuleCache = require './rebuild-module-cache'
request = require './request'

module.exports =
class Install extends Command
  @commandNames: ['install']

  constructor: ->
    @atomDirectory = config.getAtomDirectory()
    @atomPackagesDirectory = path.join(@atomDirectory, 'packages')
    @atomNodeDirectory = path.join(@atomDirectory, '.node-gyp')
    @atomNpmPath = require.resolve('npm/bin/npm-cli')
    @atomNodeGypPath = require.resolve('npm/node_modules/node-gyp/bin/node-gyp')

  parseOptions: (argv) ->
    options = optimist(argv)
    options.usage """

      Usage: apm install [<package_name>...]
             apm install <package_name>@<package_version>
             apm install --packages-file my-packages.txt

      Install the given Atom package to ~/.atom/packages/<package_name>.

      If no package name is given then all the dependencies in the package.json
      file are installed to the node_modules folder in the current working
      directory.

      A packages file can be specified that is a newline separated list of
      package names to install with optional versions using the
      `package-name@version` syntax.
    """
    options.alias('c', 'compatible').string('compatible').describe('compatible', 'Only list packages/themes compatible with this Atom version')
    options.alias('h', 'help').describe('help', 'Print this usage message')
    options.alias('s', 'silent').boolean('silent').describe('silent', 'Set the npm log level to silent')
    options.alias('q', 'quiet').boolean('quiet').describe('quiet', 'Set the npm log level to warn')
    options.boolean('check').describe('check', 'Check that native build tools are installed')
    options.boolean('verbose').default('verbose', false).describe('verbose', 'Show verbose debug information')
    options.string('packages-file').describe('packages-file', 'A text file containing the packages to install')

  installNode: (callback) =>
    installNodeArgs = ['install']
    installNodeArgs.push("--target=#{config.getNodeVersion()}")
    installNodeArgs.push("--dist-url=#{config.getNodeUrl()}")
    installNodeArgs.push("--arch=#{config.getNodeArch()}")
    installNodeArgs.push("--ensure")

    env = _.extend({}, process.env, HOME: @atomNodeDirectory)
    env.USERPROFILE = env.HOME if config.isWin32()

    fs.makeTreeSync(@atomDirectory)

    config.loadNpm (error, npm) =>
      # node-gyp doesn't currently have an option for this so just set the
      # environment variable to bypass strict SSL
      # https://github.com/TooTallNate/node-gyp/issues/448
      useStrictSsl = npm.config.get('strict-ssl') ? true
      env.NODE_TLS_REJECT_UNAUTHORIZED = 0 unless useStrictSsl

      # Pass through configured proxy to node-gyp
      proxy = npm.config.get('https-proxy') or npm.config.get('proxy')
      installNodeArgs.push("--proxy=#{proxy}") if proxy

      opts = {env, cwd: @atomDirectory}
      opts.streaming = true if @verbose

      @fork @atomNodeGypPath, installNodeArgs, opts, (code, stderr='', stdout='') ->
        if code is 0
          callback()
        else
          callback("#{stdout}\n#{stderr}")

  updateWindowsEnv: (env) ->
    env.USERPROFILE = env.HOME

    # Make sure node-gyp is always on the PATH
    localModuleBins = path.resolve(__dirname, '..', 'node_modules', '.bin')
    if env.Path
      env.Path += "#{path.delimiter}#{localModuleBins}"
    else
      env.Path = localModuleBins

  addNodeBinToEnv: (env) ->
    nodeBinFolder = path.resolve(__dirname, '..', 'bin')
    pathKey = if config.isWin32() then 'Path' else 'PATH'
    if env[pathKey]
      env[pathKey] = "#{nodeBinFolder}#{path.delimiter}#{env[pathKey]}"
    else
      env[pathKey]= nodeBinFolder

  installModule: (options, pack, modulePath, callback) ->
    installArgs = ['--globalconfig', config.getGlobalConfigPath(), '--userconfig', config.getUserConfigPath(), 'install']
    installArgs.push(modulePath)
    installArgs.push("--target=#{config.getNodeVersion()}")
    installArgs.push("--arch=#{config.getNodeArch()}")
    installArgs.push('--silent') if options.argv.silent
    installArgs.push('--quiet') if options.argv.quiet

    if vsArgs = @getVisualStudioFlags()
      installArgs.push(vsArgs)

    env = _.extend({}, process.env, HOME: @atomNodeDirectory)
    @updateWindowsEnv(env) if config.isWin32()
    @addNodeBinToEnv(env)
    installOptions = {env}
    installOptions.streaming = true if @verbose

    installGlobally = options.installGlobally ? true
    if installGlobally
      installDirectory = temp.mkdirSync('apm-install-dir-')
      nodeModulesDirectory = path.join(installDirectory, 'node_modules')
      fs.makeTreeSync(nodeModulesDirectory)
      installOptions.cwd = installDirectory

    @fork @atomNpmPath, installArgs, installOptions, (code, stderr='', stdout='') =>
      if code is 0
        if installGlobally
          commands = []
          for child in fs.readdirSync(nodeModulesDirectory)
            source = path.join(nodeModulesDirectory, child)
            destination = path.join(@atomPackagesDirectory, child)
            do (source, destination) ->
              commands.push (callback) -> fs.cp(source, destination, callback)

          commands.push (callback) => @buildModuleCache(pack.name, callback)
          commands.push (callback) => @warmCompileCache(pack.name, callback)

          async.waterfall commands, (error) =>
            if error?
              @logFailure()
            else
              @logSuccess()
            callback(error)
        else
          callback()
      else
        if installGlobally
          fs.removeSync(installDirectory)
          @logFailure()

        callback("#{stdout}\n#{stderr}")

  getVisualStudioFlags: ->
    if vsVersion = config.getInstalledVisualStudioFlag()
      "--msvs_version=#{vsVersion}"

  installModules: (options, callback) =>
    process.stdout.write 'Installing modules '

    @forkInstallCommand options, (args...) =>
      @logCommandResults(callback, args...)

  forkInstallCommand: (options, callback) ->
    installArgs = ['--globalconfig', config.getGlobalConfigPath(), '--userconfig', config.getUserConfigPath(), 'install']
    installArgs.push("--target=#{config.getNodeVersion()}")
    installArgs.push("--arch=#{config.getNodeArch()}")
    installArgs.push('--silent') if options.argv.silent
    installArgs.push('--quiet') if options.argv.quiet

    if vsArgs = @getVisualStudioFlags()
      installArgs.push(vsArgs)

    env = _.extend({}, process.env, HOME: @atomNodeDirectory)
    @updateWindowsEnv(env) if config.isWin32()
    @addNodeBinToEnv(env)
    installOptions = {env}
    installOptions.cwd = options.cwd if options.cwd
    installOptions.streaming = true if @verbose

    @fork(@atomNpmPath, installArgs, installOptions, callback)

  # Request package information from the atom.io API for a given package name.
  #
  # packageName - The string name of the package to request.
  # callback - The function to invoke when the request completes with an error
  #            as the first argument and an object as the second.
  requestPackage: (packageName, callback) ->
    requestSettings =
      url: "#{config.getAtomPackagesUrl()}/#{packageName}"
      json: true
    request.get requestSettings, (error, response, body={}) ->
      if error?<commit_msg>Add --production option to apm install<commit_after>path = require 'path'

async = require 'async'
_ = require 'underscore-plus'
optimist = require 'optimist'
CSON = require 'season'
semver = require 'npm/node_modules/semver'
temp = require 'temp'

config = require './apm'
Command = require './command'
fs = require './fs'
RebuildModuleCache = require './rebuild-module-cache'
request = require './request'

module.exports =
class Install extends Command
  @commandNames: ['install']

  constructor: ->
    @atomDirectory = config.getAtomDirectory()
    @atomPackagesDirectory = path.join(@atomDirectory, 'packages')
    @atomNodeDirectory = path.join(@atomDirectory, '.node-gyp')
    @atomNpmPath = require.resolve('npm/bin/npm-cli')
    @atomNodeGypPath = require.resolve('npm/node_modules/node-gyp/bin/node-gyp')

  parseOptions: (argv) ->
    options = optimist(argv)
    options.usage """

      Usage: apm install [<package_name>...]
             apm install <package_name>@<package_version>
             apm install --packages-file my-packages.txt

      Install the given Atom package to ~/.atom/packages/<package_name>.

      If no package name is given then all the dependencies in the package.json
      file are installed to the node_modules folder in the current working
      directory.

      A packages file can be specified that is a newline separated list of
      package names to install with optional versions using the
      `package-name@version` syntax.
    """
    options.alias('c', 'compatible').string('compatible').describe('compatible', 'Only list packages/themes compatible with this Atom version')
    options.alias('h', 'help').describe('help', 'Print this usage message')
    options.alias('s', 'silent').boolean('silent').describe('silent', 'Set the npm log level to silent')
    options.alias('q', 'quiet').boolean('quiet').describe('quiet', 'Set the npm log level to warn')
    options.boolean('check').describe('check', 'Check that native build tools are installed')
    options.boolean('verbose').default('verbose', false).describe('verbose', 'Show verbose debug information')
    options.string('packages-file').describe('packages-file', 'A text file containing the packages to install')
    options.boolean('production').describe('production', 'Do not install dev depedencies')

  installNode: (callback) =>
    installNodeArgs = ['install']
    installNodeArgs.push("--target=#{config.getNodeVersion()}")
    installNodeArgs.push("--dist-url=#{config.getNodeUrl()}")
    installNodeArgs.push("--arch=#{config.getNodeArch()}")
    installNodeArgs.push("--ensure")

    env = _.extend({}, process.env, HOME: @atomNodeDirectory)
    env.USERPROFILE = env.HOME if config.isWin32()

    fs.makeTreeSync(@atomDirectory)

    config.loadNpm (error, npm) =>
      # node-gyp doesn't currently have an option for this so just set the
      # environment variable to bypass strict SSL
      # https://github.com/TooTallNate/node-gyp/issues/448
      useStrictSsl = npm.config.get('strict-ssl') ? true
      env.NODE_TLS_REJECT_UNAUTHORIZED = 0 unless useStrictSsl

      # Pass through configured proxy to node-gyp
      proxy = npm.config.get('https-proxy') or npm.config.get('proxy')
      installNodeArgs.push("--proxy=#{proxy}") if proxy

      opts = {env, cwd: @atomDirectory}
      opts.streaming = true if @verbose

      @fork @atomNodeGypPath, installNodeArgs, opts, (code, stderr='', stdout='') ->
        if code is 0
          callback()
        else
          callback("#{stdout}\n#{stderr}")

  updateWindowsEnv: (env) ->
    env.USERPROFILE = env.HOME

    # Make sure node-gyp is always on the PATH
    localModuleBins = path.resolve(__dirname, '..', 'node_modules', '.bin')
    if env.Path
      env.Path += "#{path.delimiter}#{localModuleBins}"
    else
      env.Path = localModuleBins

  addNodeBinToEnv: (env) ->
    nodeBinFolder = path.resolve(__dirname, '..', 'bin')
    pathKey = if config.isWin32() then 'Path' else 'PATH'
    if env[pathKey]
      env[pathKey] = "#{nodeBinFolder}#{path.delimiter}#{env[pathKey]}"
    else
      env[pathKey]= nodeBinFolder

  installModule: (options, pack, modulePath, callback) ->
    installArgs = ['--globalconfig', config.getGlobalConfigPath(), '--userconfig', config.getUserConfigPath(), 'install']
    installArgs.push(modulePath)
    installArgs.push("--target=#{config.getNodeVersion()}")
    installArgs.push("--arch=#{config.getNodeArch()}")
    installArgs.push('--silent') if options.argv.silent
    installArgs.push('--quiet') if options.argv.quiet
    installArgs.push('--production') if options.argv.production

    if vsArgs = @getVisualStudioFlags()
      installArgs.push(vsArgs)

    env = _.extend({}, process.env, HOME: @atomNodeDirectory)
    @updateWindowsEnv(env) if config.isWin32()
    @addNodeBinToEnv(env)
    installOptions = {env}
    installOptions.streaming = true if @verbose

    installGlobally = options.installGlobally ? true
    if installGlobally
      installDirectory = temp.mkdirSync('apm-install-dir-')
      nodeModulesDirectory = path.join(installDirectory, 'node_modules')
      fs.makeTreeSync(nodeModulesDirectory)
      installOptions.cwd = installDirectory

    @fork @atomNpmPath, installArgs, installOptions, (code, stderr='', stdout='') =>
      if code is 0
        if installGlobally
          commands = []
          for child in fs.readdirSync(nodeModulesDirectory)
            source = path.join(nodeModulesDirectory, child)
            destination = path.join(@atomPackagesDirectory, child)
            do (source, destination) ->
              commands.push (callback) -> fs.cp(source, destination, callback)

          commands.push (callback) => @buildModuleCache(pack.name, callback)
          commands.push (callback) => @warmCompileCache(pack.name, callback)

          async.waterfall commands, (error) =>
            if error?
              @logFailure()
            else
              @logSuccess()
            callback(error)
        else
          callback()
      else
        if installGlobally
          fs.removeSync(installDirectory)
          @logFailure()

        callback("#{stdout}\n#{stderr}")

  getVisualStudioFlags: ->
    if vsVersion = config.getInstalledVisualStudioFlag()
      "--msvs_version=#{vsVersion}"

  installModules: (options, callback) =>
    process.stdout.write 'Installing modules '

    @forkInstallCommand options, (args...) =>
      @logCommandResults(callback, args...)

  forkInstallCommand: (options, callback) ->
    installArgs = ['--globalconfig', config.getGlobalConfigPath(), '--userconfig', config.getUserConfigPath(), 'install']
    installArgs.push("--target=#{config.getNodeVersion()}")
    installArgs.push("--arch=#{config.getNodeArch()}")
    installArgs.push('--silent') if options.argv.silent
    installArgs.push('--quiet') if options.argv.quiet
    installArgs.push('--production') if options.argv.production

    if vsArgs = @getVisualStudioFlags()
      installArgs.push(vsArgs)

    env = _.extend({}, process.env, HOME: @atomNodeDirectory)
    @updateWindowsEnv(env) if config.isWin32()
    @addNodeBinToEnv(env)
    installOptions = {env}
    installOptions.cwd = options.cwd if options.cwd
    installOptions.streaming = true if @verbose

    @fork(@atomNpmPath, installArgs, installOptions, callback)

  # Request package information from the atom.io API for a given package name.
  #
  # packageName - The string name of the package to request.
  # callback - The function to invoke when the request completes with an error
  #            as the first argument and an object as the second.
  requestPackage: (packageName, callback) ->
    requestSettings =
      url: "#{config.getAtomPackagesUrl()}/#{packageName}"
      json: true
    request.get requestSettings, (error, response, body={}) ->
      if error?
{'fraction_non_alphanumeric': 0.131463003757937, 'fraction_numerical': 0.0018141764934560061, 'mean_word_length': 3.4226361031518624, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 51, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7514010', 'n_tokens_mistral': 4510, 'n_tokens_neox': 4390, 'n_words': 1086}