Skip to content

Commit

Permalink
Merge pull request #325 from mousetraps/i297
Browse files Browse the repository at this point in the history
Fix #297 add support for relative node.exe paths

Additionally...
* Consolidated Node exe path resolution to reduce code duplication and fix other issues due to path resolution inconsistencies
* added basic node.exe path resolution unit tests
  • Loading branch information
mousetraps committed Aug 4, 2015
2 parents 5b5400a + 889751a commit 4f187a3
Show file tree
Hide file tree
Showing 17 changed files with 470 additions and 379 deletions.
3 changes: 1 addition & 2 deletions Common/Product/SharedProject/CommonProjectNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1539,8 +1539,7 @@ protected override ConfigProvider CreateConfigProvider() {
/// Returns resolved value of the current working directory property.
/// </summary>
public string GetWorkingDirectory() {
string workDir = GetProjectProperty(CommonConstants.WorkingDirectory, resetCache: false);

string workDir = CommonUtils.UnquotePath(GetProjectProperty(CommonConstants.WorkingDirectory, resetCache: false));
return CommonUtils.GetAbsoluteDirectoryPath(ProjectHome, workDir);
}

Expand Down
12 changes: 12 additions & 0 deletions Common/Product/SharedProject/CommonUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,18 @@ private static string TrimUpPaths(string path) {
return (actualStart > 0) ? path.Substring(actualStart) : path;
}

/// <summary>
/// Remove first and last quotes from path
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static string UnquotePath(string path) {
if (path.StartsWith("\"") && path.EndsWith("\"")) {
return path.Substring(1, path.Length - 2);
}
return path;
}

/// <summary>
/// Returns true if the path is a valid path, regardless of whether the
/// file exists or not.
Expand Down
266 changes: 140 additions & 126 deletions Nodejs/Product/Nodejs/Nodejs.cs
Original file line number Diff line number Diff line change
@@ -1,126 +1,140 @@
//*********************************************************//
// Copyright (c) Microsoft. All rights reserved.
//
// Apache 2.0 License
//
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
//*********************************************************//

using System;
using System.Diagnostics;
using System.IO;
#if !NO_WINDOWS
using System.Windows.Forms;
#endif
using Microsoft.Win32;
#if !NO_WINDOWS
using Microsoft.NodejsTools.Project;
using System.Security;
#endif

namespace Microsoft.NodejsTools {
public sealed class Nodejs {
private const string NodejsRegPath = "Software\\Node.js";
private const string InstallPath = "InstallPath";

public static string NodeExePath {
get {
return GetPathToNodeExecutable();
}
}

public static string GetPathToNodeExecutable(string executable = "node.exe") {
// Attempt to find Node.js/NPM in the Registry. (Currrent User)
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default))
using (var node = baseKey.OpenSubKey(NodejsRegPath)) {
if (node != null) {
string key = (node.GetValue(InstallPath) as string) ?? string.Empty;
var execPath = Path.Combine(key, executable);
if (File.Exists(execPath)) {
return execPath;
}
}
}

// Attempt to find Node.js/NPM in the Registry. (Local Machine x64)
if (Environment.Is64BitOperatingSystem) {
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
using (var node64 = baseKey.OpenSubKey(NodejsRegPath)) {
if (node64 != null) {
string key = (node64.GetValue(InstallPath) as string) ?? string.Empty;
var execPath = Path.Combine(key, executable);
if (File.Exists(execPath)) {
return execPath;
}
}
}
}

// Attempt to find Node.js/NPM in the Registry. (Local Machine x86)
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32))
using (var node = baseKey.OpenSubKey(NodejsRegPath)) {
if (node != null) {
string key = (node.GetValue(InstallPath) as string) ?? string.Empty;
var execPath = Path.Combine(key, executable);
if (File.Exists(execPath)) {
return execPath;
}
}
}

// If we didn't find node.js in the registry we should look at the user's path.
foreach (var dir in Environment.GetEnvironmentVariable("PATH").Split(Path.PathSeparator)) {
var execPath = Path.Combine(dir, executable);
if (File.Exists(execPath)) {
return execPath;
}
}

// It wasn't in the users path. Check Program Files for the nodejs folder.
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "nodejs", executable);
if (File.Exists(path)) {
return path;
}

// It wasn't in the users path. Check Program Files x86 for the nodejs folder.
var x86path = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
if (!String.IsNullOrEmpty(x86path)) {
path = Path.Combine(x86path, "nodejs", executable);
if (File.Exists(path)) {
return path;
}
}

// we didn't find the path.
return null;
}

#if !NO_WINDOWS
public static void ShowNodejsNotInstalled() {
MessageBox.Show(
SR.GetString(SR.NodejsNotInstalled),
SR.ProductName,
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}

public static void ShowNodejsPathNotFound(string path) {
MessageBox.Show(
SR.GetString(SR.NodeExeDoesntExist, path),
SR.ProductName,
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
#endif
}
}
//*********************************************************//
// Copyright (c) Microsoft. All rights reserved.
//
// Apache 2.0 License
//
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
//*********************************************************//

using System;
using System.IO;
#if !NO_WINDOWS
using System.Windows.Forms;
#endif
using Microsoft.Win32;
#if !NO_WINDOWS
using Microsoft.NodejsTools.Project;
#endif
using Microsoft.VisualStudioTools;

namespace Microsoft.NodejsTools {
public sealed class Nodejs {
private const string NodejsRegPath = "Software\\Node.js";
private const string InstallPath = "InstallPath";

public static string NodeExePath {
get {
return GetPathToNodeExecutableFromEnvironment();
}
}

public static string GetAbsoluteNodeExePath(string root, string relativePath) {
var overridePath = CommonUtils.UnquotePath(relativePath ?? string.Empty);
if (!String.IsNullOrWhiteSpace(overridePath)) {
if (string.IsNullOrWhiteSpace(root)) {
return relativePath;
}
try {
return CommonUtils.GetAbsoluteFilePath(root, overridePath);
} catch (InvalidOperationException) {
return relativePath;
}
}
return NodeExePath;
}

public static string GetPathToNodeExecutableFromEnvironment(string executable = "node.exe") {
// Attempt to find Node.js/NPM in the Registry. (Currrent User)
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default))
using (var node = baseKey.OpenSubKey(NodejsRegPath)) {
if (node != null) {
string key = (node.GetValue(InstallPath) as string) ?? string.Empty;
var execPath = Path.Combine(key, executable);
if (File.Exists(execPath)) {
return execPath;
}
}
}

// Attempt to find Node.js/NPM in the Registry. (Local Machine x64)
if (Environment.Is64BitOperatingSystem) {
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
using (var node64 = baseKey.OpenSubKey(NodejsRegPath)) {
if (node64 != null) {
string key = (node64.GetValue(InstallPath) as string) ?? string.Empty;
var execPath = Path.Combine(key, executable);
if (File.Exists(execPath)) {
return execPath;
}
}
}
}

// Attempt to find Node.js/NPM in the Registry. (Local Machine x86)
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32))
using (var node = baseKey.OpenSubKey(NodejsRegPath)) {
if (node != null) {
string key = (node.GetValue(InstallPath) as string) ?? string.Empty;
var execPath = Path.Combine(key, executable);
if (File.Exists(execPath)) {
return execPath;
}
}
}

// If we didn't find node.js in the registry we should look at the user's path.
foreach (var dir in Environment.GetEnvironmentVariable("PATH").Split(Path.PathSeparator)) {
var execPath = Path.Combine(dir, executable);
if (File.Exists(execPath)) {
return execPath;
}
}

// It wasn't in the users path. Check Program Files for the nodejs folder.
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "nodejs", executable);
if (File.Exists(path)) {
return path;
}

// It wasn't in the users path. Check Program Files x86 for the nodejs folder.
var x86path = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
if (!String.IsNullOrEmpty(x86path)) {
path = Path.Combine(x86path, "nodejs", executable);
if (File.Exists(path)) {
return path;
}
}

// we didn't find the path.
return null;
}

#if !NO_WINDOWS
public static void ShowNodejsNotInstalled() {
MessageBox.Show(
SR.GetString(SR.NodejsNotInstalled),
SR.ProductName,
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}

public static void ShowNodejsPathNotFound(string path) {
MessageBox.Show(
SR.GetString(SR.NodeExeDoesntExist, path),
SR.ProductName,
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Microsoft.VisualStudioTools;
using Microsoft.VisualStudioTools.Project;


namespace Microsoft.NodejsTools.Project {
partial class NodejsGeneralPropertyPageControl : UserControl {
private readonly NodejsGeneralPropertyPage _propPage;
Expand Down Expand Up @@ -169,7 +170,8 @@ private void SetCueBanner() {
}

private void NodeExePathChanged(object sender, EventArgs e) {
if (String.IsNullOrEmpty(_nodeExePath.Text) || _nodeExePath.Text.Contains("$(") || File.Exists(_nodeExePath.Text)) {
if (String.IsNullOrEmpty(_nodeExePath.Text) || _nodeExePath.Text.Contains("$(") ||
File.Exists(Nodejs.GetAbsoluteNodeExePath(_propPage.Project.ProjectHome, _nodeExePath.Text))) {
_nodeExeErrorProvider.SetError(_nodeExePath, String.Empty);
} else {
_nodeExeErrorProvider.SetError(_nodeExePath, SR.GetString(SR.NodeExePathNotFound));
Expand Down Expand Up @@ -200,7 +202,6 @@ private void BrowseDirectoryClick(object sender, EventArgs e) {

private void PortChanged(object sender, EventArgs e) {
var textSender = (TextBox)sender;

if (!textSender.Text.Contains("$(") &&
textSender.Text.Any(ch => !Char.IsDigit(ch))) {
_nodeExeErrorProvider.SetError(textSender, SR.GetString(SR.InvalidPortNumber));
Expand Down
16 changes: 3 additions & 13 deletions Nodejs/Product/Nodejs/Project/NodejsProjectLauncher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,7 @@ private string GetFullArguments(string file, bool includeNodeArgs = true) {

private string GetNodePath() {
var overridePath = _project.GetProjectProperty(NodejsConstants.NodeExePath);
if (!String.IsNullOrWhiteSpace(overridePath)) {
return overridePath;
}
return Nodejs.NodeExePath;
return Nodejs.GetAbsoluteNodeExePath(_project.ProjectHome, overridePath);
}

#endregion
Expand Down Expand Up @@ -204,22 +201,15 @@ private void StartWithDebugger(string startupFile) {


private void LaunchDebugger(IServiceProvider provider, VsDebugTargetInfo dbgInfo) {
if (!Directory.Exists(UnquotePath(dbgInfo.bstrCurDir))) {
if (!Directory.Exists(dbgInfo.bstrCurDir)) {
MessageBox.Show(String.Format("Working directory \"{0}\" does not exist.", dbgInfo.bstrCurDir), "Node.js Tools for Visual Studio");
} else if (!File.Exists(UnquotePath(dbgInfo.bstrExe))) {
} else if (!File.Exists(dbgInfo.bstrExe)) {
MessageBox.Show(String.Format("Interpreter \"{0}\" does not exist.", dbgInfo.bstrExe), "Node.js Tools for Visual Studio");
} else if (DoesProjectSupportDebugging()) {
VsShellUtilities.LaunchDebugger(provider, dbgInfo);
}
}

private static string UnquotePath(string p) {
if (p.StartsWith("\"") && p.EndsWith("\"")) {
return p.Substring(1, p.Length - 2);
}
return p;
}

private bool DoesProjectSupportDebugging() {
var typeScriptOutFile = _project.GetProjectProperty("TypeScriptOutFile");
if (!string.IsNullOrEmpty(typeScriptOutFile)) {
Expand Down
12 changes: 10 additions & 2 deletions Nodejs/Product/Nodejs/Project/NodejsProjectNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,11 @@ internal override int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecop
}
} else if (cmdGroup == Guids.NodejsNpmCmdSet) {
try {
NpmHelpers.GetPathToNpm(this.Project.GetNodejsProject().GetProjectProperty(NodejsConstants.NodeExePath));
NpmHelpers.GetPathToNpm(
Nodejs.GetAbsoluteNodeExePath(
ProjectHome,
Project.GetNodejsProject().GetProjectProperty(NodejsConstants.NodeExePath)
));
} catch (NpmNotFoundException) {
Nodejs.ShowNodejsNotInstalled();
return VSConstants.S_OK;
Expand All @@ -976,7 +980,11 @@ internal override int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecop
protected override int ExecCommandThatDependsOnSelectedNodes(Guid cmdGroup, uint cmdId, uint cmdExecOpt, IntPtr vaIn, IntPtr vaOut, CommandOrigin commandOrigin, IList<HierarchyNode> selectedNodes, out bool handled) {
if (cmdGroup == Guids.NodejsNpmCmdSet) {
try {
NpmHelpers.GetPathToNpm(this.Project.GetNodejsProject().GetProjectProperty(NodejsConstants.NodeExePath));
NpmHelpers.GetPathToNpm(
Nodejs.GetAbsoluteNodeExePath(
ProjectHome,
Project.GetNodejsProject().GetProjectProperty(NodejsConstants.NodeExePath)
));
} catch (NpmNotFoundException) {
Nodejs.ShowNodejsNotInstalled();
handled = true;
Expand Down
Loading

0 comments on commit 4f187a3

Please sign in to comment.