private DirectoryPath GetToolsLocalInstallPath(PackageReference package)
        {
            var toolsFolder = _fileSystem.GetDirectory(
                _configuration.GetToolPath(_environment.WorkingDirectory, _environment));

            if (!toolsFolder.Exists)
            {
                toolsFolder.Create();
            }

            var modules = toolsFolder.Path.Combine("./node_modules/");

            return(GetPackagePath(modules, package));
        }
        /// <summary>
        /// Installs the specified resource at the given location.
        /// </summary>
        /// <param name="package">The package reference.</param>
        /// <param name="type">The package type.</param>
        /// <param name="path">The location where to install the package.</param>
        /// <returns>The installed files.</returns>
        public IReadOnlyCollection <IFile> Install(PackageReference package, PackageType type, DirectoryPath path)
        {
            if (package == null)
            {
                throw new ArgumentNullException(nameof(package));
            }

            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            // We are going to assume that the default install location is the
            // currently configured location for the Cake Tools Folder
            var toolsFolderDirectoryPath = _config.GetToolPath(_environment.WorkingDirectory, _environment);

            _log.Debug("Configured Tools Folder: {0}", toolsFolderDirectoryPath);

            var toolLocation = toolsFolderDirectoryPath.FullPath;

            if (package.Parameters.ContainsKey("global"))
            {
                toolLocation = "global";
            }

            // First we need to check if the Tool is already installed
            var installedToolNames = GetInstalledTools(toolLocation);

            _log.Debug("Checking for tool: {0}", package.Package.ToLowerInvariant());
            if (installedToolNames.Contains(package.Package.ToLowerInvariant()))
            {
                _log.Information("Tool {0} is already installed, so nothing to do here.", package.Package);
            }
            else
            {
                InstallTool(package, toolsFolderDirectoryPath);
            }

            var result = _contentResolver.GetFiles(package, type);

            if (result.Count == 0)
            {
                const string format = "Could not find any relevant files for tool '{0}'. Perhaps you need an include parameter?";
                _log.Warning(format, package.Package);
            }

            return(result);
        }
        /// <summary>
        /// Installs the specified resource.
        /// </summary>
        /// <param name="package">The package resource.</param>
        /// <param name="type">The package type.</param>
        /// <param name="path">The location where to install the resource.</param>
        /// <returns>The installed files.</returns>
        public IReadOnlyCollection <IFile> Install(PackageReference package, PackageType type, DirectoryPath path)
        {
            if (package == null)
            {
                throw new ArgumentNullException(nameof(package));
            }

            // find npm
            _log.Debug("looking for npm.cmd");
            var npmTool = _toolLocator.Resolve("npm.cmd");

            if (npmTool == null)
            {
                _log.Debug("looking for npm");
                npmTool = _toolLocator.Resolve("npm");
            }

            if (npmTool == null)
            {
                throw new FileNotFoundException("npm could not be found.");
            }

            _log.Debug("Found npm at {0}", npmTool);

            // Install the package.
            _log.Debug("Installing package {0} with npm...", package.Package);
            var workingDir = _environment.WorkingDirectory;

            if (GetModuleInstallationLocation(package) == ModulesInstallationLocation.Tools)
            {
                var toolsFolder = _fileSystem.GetDirectory(
                    _config.GetToolPath(_environment.WorkingDirectory, _environment));

                if (!toolsFolder.Exists)
                {
                    toolsFolder.Create();
                }

                _log.Debug("Installing into cake-tools location: {0}", package.Package);
                workingDir = toolsFolder.Path;
            }

            var process = _processRunner.Start(
                npmTool.FullPath,
                new ProcessSettings
            {
                Arguments              = GetArguments(package, _config),
                WorkingDirectory       = workingDir,
                RedirectStandardOutput = true,
                Silent = _log.Verbosity < Verbosity.Diagnostic,
            });

            process.WaitForExit();

            var exitCode = process.GetExitCode();

            if (exitCode != 0)
            {
                _log.Warning("npm exited with {0}", exitCode);
                var output = string.Join(Environment.NewLine, process.GetStandardOutput());
                _log.Verbose(Verbosity.Diagnostic, "Output:\r\n{0}", output);
            }

            var result = _contentResolver.GetFiles(package, type, GetModuleInstallationLocation(package));

            if (result.Count != 0)
            {
                return(result);
            }

            _log.Warning("Could not determine installed package files! Installation may not be complete.");

            // TODO: maybe some warnings here
            return(result);
        }
        /// <summary>
        /// Installs the specified resource at the given location.
        /// </summary>
        /// <param name="package">The package reference.</param>
        /// <param name="type">The package type.</param>
        /// <param name="path">The location where to install the package.</param>
        /// <returns>The installed files.</returns>
        public IReadOnlyCollection <IFile> Install(PackageReference package, PackageType type, DirectoryPath path)
        {
            if (package == null)
            {
                throw new ArgumentNullException(nameof(package));
            }

            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            // We are going to assume that the default install location is the
            // currently configured location for the Cake Tools Folder
            var toolsFolderDirectoryPath = _config.GetToolPath(_environment.WorkingDirectory, _environment);

            _log.Debug("Configured Tools Folder: {0}", toolsFolderDirectoryPath);

            var toolLocation = toolsFolderDirectoryPath.FullPath;

            if (package.Parameters.ContainsKey("global"))
            {
                toolLocation = "global";
            }

            // First we need to check if the Tool is already installed
            var installedTools = GetInstalledTools(toolLocation);

            _log.Debug("Checking for tool: {0}", package.Package.ToLowerInvariant());

            var installedTool = installedTools.FirstOrDefault(t => t.Id.ToLowerInvariant() == package.Package.ToLowerInvariant());

            if (installedTool != null)
            {
                // The tool is already installed, so need to check if requested version is the same as
                // what is already installed
                string requestedVersion = null;

                if (package.Parameters.ContainsKey("version"))
                {
                    requestedVersion = package.Parameters["version"].First();
                }

                if (requestedVersion == null)
                {
                    _log.Warning("Tool {0} is already installed, and no specific version has been requested via pre-processor directive, so leaving current version installed.", package.Package);
                }
                else if (requestedVersion.ToLowerInvariant() != installedTool.Version.ToLowerInvariant())
                {
                    _log.Warning("Tool {0} is already installed, but a different version has been requested.  Uninstall/install will now be performed...", package.Package);
                    RunDotNetTool(package, toolsFolderDirectoryPath, DotNetToolOperation.Uninstall);
                    RunDotNetTool(package, toolsFolderDirectoryPath, DotNetToolOperation.Install);
                }
                else
                {
                    _log.Information("Tool {0} is already installed, with required version.", package.Package);
                }
            }
            else
            {
                // The tool isn't already installed, go ahead and install it
                RunDotNetTool(package, toolsFolderDirectoryPath, DotNetToolOperation.Install);
            }

            var result = _contentResolver.GetFiles(package, type);

            if (result.Count == 0)
            {
                const string format = "Could not find any relevant files for tool '{0}'. Perhaps you need an include parameter?";
                _log.Warning(format, package.Package);
            }

            return(result);
        }