diff --git a/Nodejs/Product/Nodejs/NodejsConstants.cs b/Nodejs/Product/Nodejs/NodejsConstants.cs index 8c53edd2d..ff83ab9bb 100644 --- a/Nodejs/Product/Nodejs/NodejsConstants.cs +++ b/Nodejs/Product/Nodejs/NodejsConstants.cs @@ -15,6 +15,7 @@ //*********************************************************// using System; +using System.IO; namespace Microsoft.NodejsTools { internal class NodejsConstants { @@ -73,6 +74,33 @@ internal class NodejsConstants { internal const string NodeToolsProcessIdEnvironmentVariable = "_NTVS_PID"; + public static string NtvsLocalAppData { + get { + return Path.Combine( + System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), + "Microsoft", + "Node.js Tools"); + } + } + + /// + /// Path to the private package where NTVS tools are installed. + /// + public static string ExternalToolsPath { + get { + return Path.Combine(NtvsLocalAppData, "ExternalTools"); + } + + } + /// + /// Path to where NTVS caches Npm data. + /// + public static string NpmCachePath { + get { + return Path.Combine(NtvsLocalAppData, "NpmCache"); + } + } + /// /// Checks whether a relative and double-backslashed seperated path contains a folder name. /// diff --git a/Nodejs/Product/Nodejs/Options/NodejsNpmOptionsControl.cs b/Nodejs/Product/Nodejs/Options/NodejsNpmOptionsControl.cs index 5f78f45ce..5cfb86b6e 100644 --- a/Nodejs/Product/Nodejs/Options/NodejsNpmOptionsControl.cs +++ b/Nodejs/Product/Nodejs/Options/NodejsNpmOptionsControl.cs @@ -15,25 +15,20 @@ //*********************************************************// using System; +using System.Diagnostics; using System.IO; using System.Windows.Forms; using Microsoft.NodejsTools.Project; using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudioTools; namespace Microsoft.NodejsTools.Options { public partial class NodejsNpmOptionsControl : UserControl { - - private string _npmCachePath; - public NodejsNpmOptionsControl() { InitializeComponent(); } - internal void SyncControlWithPageSettings(NodejsNpmOptionsPage page) { _showOutputWhenRunningNpm.Checked = page.ShowOutputWindowWhenExecutingNpm; - _npmCachePath = page.NpmCachePath; _cacheClearedSuccessfully.Visible = false; } @@ -42,34 +37,57 @@ internal void SyncPageWithControlSettings(NodejsNpmOptionsPage page) { } private void ClearCacheButton_Click(object sender, EventArgs e) { + bool didClearNpmCache = TryDeleteCacheDirectory(NodejsConstants.NpmCachePath); + bool didClearTools = TryDeleteCacheDirectory(NodejsConstants.ExternalToolsPath); + + if (!didClearNpmCache || !didClearTools) { + MessageBox.Show( + SR.GetString(SR.CacheDirectoryClearFailedCaption, NodejsConstants.NtvsLocalAppData), + SR.GetString(SR.CacheDirectoryClearFailedTitle), + MessageBoxButtons.OK, + MessageBoxIcon.Information); + } + + _cacheClearedSuccessfully.Visible = didClearNpmCache && didClearTools; + } + + private static bool TryDeleteCacheDirectory(string cachePath) { + if (!Directory.Exists(cachePath)) { + return true; + } + try { - Directory.Delete(_npmCachePath, true); - _cacheClearedSuccessfully.Visible = true; - } catch (DirectoryNotFoundException) { - // Directory has already been deleted. Do nothing. - _cacheClearedSuccessfully.Visible = true; - } catch (IOException exception) { + // To handle long paths, nuke the directory contents with robocopy + string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempDirectory); + var psi = new ProcessStartInfo("cmd.exe", string.Format(@"/C robocopy /mir ""{0}"" ""{1}""", tempDirectory, cachePath)) { + UseShellExecute = false, + CreateNoWindow = true + }; + + using (var process = Process.Start(psi)) { + process.WaitForExit(10000); + } + + // Then delete the directory itself + try { + Directory.Delete(cachePath, true); + } catch (DirectoryNotFoundException) { + // noop + } + + return !Directory.Exists(cachePath); + } catch (IOException) { // files are in use or path is too long - MessageBox.Show( - string.Format("Cannot clear npm cache. {0}", exception.Message), - "Cannot Clear npm Cache", - MessageBoxButtons.OK, - MessageBoxIcon.Information - ); + return false; } catch (Exception exception) { try { ActivityLog.LogError(SR.ProductName, exception.ToString()); } catch (InvalidOperationException) { // Activity Log is unavailable. } - - MessageBox.Show( - string.Format("Cannot clear npm cache. Try manually deleting the directory: {0}", _npmCachePath), - "Cannot Clear npm Cache", - MessageBoxButtons.OK, - MessageBoxIcon.Information - ); } + return false; } } } \ No newline at end of file diff --git a/Nodejs/Product/Nodejs/Options/NodejsNpmOptionsPage.cs b/Nodejs/Product/Nodejs/Options/NodejsNpmOptionsPage.cs index 6aa9eb4c9..fdd785606 100644 --- a/Nodejs/Product/Nodejs/Options/NodejsNpmOptionsPage.cs +++ b/Nodejs/Product/Nodejs/Options/NodejsNpmOptionsPage.cs @@ -14,8 +14,6 @@ // //*********************************************************// -using System; -using System.IO; using System.Runtime.InteropServices; using System.Windows.Forms; @@ -47,17 +45,6 @@ protected override IWin32Window Window { /// public bool ShowOutputWindowWhenExecutingNpm { get; set; } - public string NpmCachePath { - get { - return Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "Microsoft", - "Node.js Tools", - "NpmCache" - ); - } - } - /// /// Resets settings back to their defaults. This should be followed by /// a call to to commit the new diff --git a/Nodejs/Product/Nodejs/Project/NodeModulesNode.cs b/Nodejs/Product/Nodejs/Project/NodeModulesNode.cs index d30721835..127223a3c 100644 --- a/Nodejs/Product/Nodejs/Project/NodeModulesNode.cs +++ b/Nodejs/Product/Nodejs/Project/NodeModulesNode.cs @@ -138,7 +138,7 @@ public string PathToNpm { private static INpmController DefaultNpmController(string projectHome, NpmPathProvider pathProvider) { return NpmControllerFactory.Create( projectHome, - NodejsPackage.Instance.NpmOptionsPage.NpmCachePath, + NodejsConstants.NpmCachePath, false, pathProvider); } diff --git a/Nodejs/Product/Nodejs/Project/ProjectResources.cs b/Nodejs/Product/Nodejs/Project/ProjectResources.cs index b80065269..bb52df5ff 100644 --- a/Nodejs/Product/Nodejs/Project/ProjectResources.cs +++ b/Nodejs/Product/Nodejs/Project/ProjectResources.cs @@ -31,6 +31,8 @@ internal class SR : CommonSR { internal const string CatalogLoadingNoNpm = "CatalogLoadingNoNpm"; internal const string CategoryStatus = "CategoryStatus"; internal const string CategoryVersion = "CategoryVersion"; + internal const string CacheDirectoryClearFailedTitle = "CacheDirectoryClearFailedTitle"; + internal const string CacheDirectoryClearFailedCaption = "CacheDirectoryClearFailedCaption"; internal const string ContinueWithoutAzureToolsUpgrade = "ContinueWithoutAzureToolsUpgrade"; internal const string DebuggerConnectionClosed = "DebuggerConnectionClosed"; internal const string DebuggerModuleUpdateFailed = "DebuggerModuleUpdateFailed"; diff --git a/Nodejs/Product/Nodejs/Resources.resx b/Nodejs/Product/Nodejs/Resources.resx index b72bcd3ec..75e2ae257 100644 --- a/Nodejs/Product/Nodejs/Resources.resx +++ b/Nodejs/Product/Nodejs/Resources.resx @@ -556,6 +556,12 @@ We recommend installing the latest version of Microsoft Azure Tools for Visual S Your version of Microsoft Azure Tools is not supported by Node.js Tools for Visual Studio. + + Could Not Clear Node.js Cache Directory + + + Could not clear Node.js cache directory. Try manually deleting the directory: {0} + &Continue Some manual steps will be required to configure your project. diff --git a/Nodejs/Product/Nodejs/TypingsAcquisition.cs b/Nodejs/Product/Nodejs/TypingsAcquisition.cs index 196566496..f1bf69f3b 100644 --- a/Nodejs/Product/Nodejs/TypingsAcquisition.cs +++ b/Nodejs/Product/Nodejs/TypingsAcquisition.cs @@ -35,26 +35,13 @@ internal class TypingsAcquisition { private static SemaphoreSlim typingsToolGlobalWorkSemaphore = new SemaphoreSlim(1); - /// - /// Path the the private package where the typings acquisition tool is installed. - /// - private static string NtvsExternalToolsPath { - get { - return Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "Microsoft", - "Node.js Tools", - "ExternalTools"); - } - } - /// /// Full path to the typings acquisition tool. /// private static string TypingsToolPath { get { return Path.Combine( - NtvsExternalToolsPath, + NodejsConstants.ExternalToolsPath, "node_modules", ".bin", TypingsToolExe); @@ -161,11 +148,11 @@ private async Task EnsureTypingsToolInstalled() { private async Task InstallTypingsTool() { _didTryToInstallTypingsTool = true; - Directory.CreateDirectory(NtvsExternalToolsPath); + Directory.CreateDirectory(NodejsConstants.ExternalToolsPath); // install typings using (var commander = _npmController.CreateNpmCommander()) { - return await commander.InstallPackageToFolderByVersionAsync(NtvsExternalToolsPath, TypingsTool, TypingsToolVersion, false); + return await commander.InstallPackageToFolderByVersionAsync(NodejsConstants.ExternalToolsPath, TypingsTool, TypingsToolVersion, false); } }