Skip to content

Commit

Permalink
Merge pull request #386 from mousetraps/npm3
Browse files Browse the repository at this point in the history
#233 npm v3 support, #308 VS2015 RTM exceptions on project load
  • Loading branch information
billti committed Aug 27, 2015
2 parents 50ee5b4 + 86a7b46 commit 05b51f5
Show file tree
Hide file tree
Showing 24 changed files with 432 additions and 120 deletions.
2 changes: 2 additions & 0 deletions Nodejs/Product/Analysis/Analysis.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
<Compile Include="Analysis\AnalysisSerializationSupportedTypeAttribute.cs" />
<Compile Include="Analysis\AnalysisValueExtensions.cs" />
<Compile Include="Analysis\Analyzer\GruntfileAnalysisUnit.cs" />
<Compile Include="Analysis\Analyzer\RequireAnalysisUnit.cs" />
<Compile Include="Analysis\IAnalysisSerializeAsNull.cs" />
<Compile Include="Analysis\AnalysisSerializer.cs" />
<Compile Include="Analysis\AnalysisLimits.cs" />
Expand Down Expand Up @@ -107,6 +108,7 @@
<Compile Include="Analysis\Analyzer\StatementEnvironmentRecord.cs" />
<Compile Include="Analysis\AssemblyInfo.cs" />
<Compile Include="Analysis\IDetachableProxy.cs" />
<Compile Include="Analysis\ModuleTreeExtensions.cs" />
<Compile Include="Analysis\NodejsModuleBuilder.Specializations.cs" />
<Compile Include="Analysis\Validation.cs" />
<Compile Include="Analysis\Values\ArgumentsValue.cs" />
Expand Down
19 changes: 3 additions & 16 deletions Nodejs/Product/Analysis/Analysis/AnalysisLimits.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,23 +157,10 @@ public static AnalysisLimits MakeLowAnalysisLimits() {
/// <summary>
/// Checks whether relative path exceed the nested module limit.
/// </summary>
/// <param name="path">Path to module file which has to be checked for depth limit.</param>
/// <param name="nestedModulesDepth">Depth of module file which has to be checked for depth limit.</param>
/// <returns>True if path too deep in nesting tree; false overwise.</returns>
public bool IsPathExceedNestingLimit(string path) {
int nestedModulesCount = 0;
int startIndex = 0;
int index = path.IndexOf(AnalysisConstants.NodeModulesFolder, startIndex, StringComparison.OrdinalIgnoreCase);
while (index != -1) {
nestedModulesCount++;
if (nestedModulesCount > this.NestedModulesLimit){
return true;
}

startIndex = index + AnalysisConstants.NodeModulesFolder.Length;
index = path.IndexOf(AnalysisConstants.NodeModulesFolder, startIndex, StringComparison.OrdinalIgnoreCase);
}

return false;
public bool IsPathExceedNestingLimit(int nestedModulesDepth) {
return nestedModulesDepth > NestedModulesLimit;
}

public override bool Equals(object obj) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
[assembly: AnalysisSerializationSupportedType(typeof(FunctionAnalysisUnit))]
[assembly: AnalysisSerializationSupportedType(typeof(CartesianProductFunctionAnalysisUnit))]
[assembly: AnalysisSerializationSupportedType(typeof(CartesianProductFunctionAnalysisUnit.CartesianLocalVariable))]
[assembly: AnalysisSerializationSupportedType(typeof(RequireAnalysisUnit))]
[assembly: AnalysisSerializationSupportedType(typeof(ReferenceDict))]
[assembly: AnalysisSerializationSupportedType(typeof(ReferenceList))]
[assembly: AnalysisSerializationSupportedType(typeof(DependentKeyValue))]
Expand Down
52 changes: 52 additions & 0 deletions Nodejs/Product/Analysis/Analysis/Analyzer/RequireAnalysisUnit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//*********************************************************//
// 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.Diagnostics;
using System.Threading;

namespace Microsoft.NodejsTools.Analysis.Analyzer {
class RequireAnalysisUnit : AnalysisUnit {
private string _dependency;
private ModuleTree _tree;
private ModuleTable _table;

internal RequireAnalysisUnit(ModuleTree tree, ModuleTable table, ProjectEntry entry, string dependency) : base (entry.Tree, entry.EnvironmentRecord) {
_tree = tree;
_table = table;
_dependency = dependency;
}

internal override void AnalyzeWorker(DDG ddg, CancellationToken cancel) {
ModuleTree module = _table.RequireModule(this, _dependency, _tree);
if (module == null) {
return;
}

AddChildVisibilitiesExcludingNodeModules(module);
}

private void AddChildVisibilitiesExcludingNodeModules(ModuleTree moduleTree) {
foreach (var childTree in moduleTree.GetChildrenExcludingNodeModules()) {
Debug.Assert(childTree.Name != AnalysisConstants.NodeModulesFolder);
if (childTree.ProjectEntry == null) {
AddChildVisibilitiesExcludingNodeModules(childTree);
} else {
_table.AddVisibility(_tree, childTree.ProjectEntry);
}
}
}
}
}
26 changes: 21 additions & 5 deletions Nodejs/Product/Analysis/Analysis/JsAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public ProjectEntry AddModule(string filePath, IAnalysisCookie cookie = null) {
return entry;
}

public IAnalyzable AddPackageJson(string filePath, string entryPoint) {
public IAnalyzable AddPackageJson(string filePath, string entryPoint, List<string> dependencies = null) {
if (!Path.GetFileName(filePath).Equals("package.json", StringComparison.OrdinalIgnoreCase)) {
throw new InvalidOperationException("path must be to package.json file");
}
Expand All @@ -140,10 +140,18 @@ public IAnalyzable AddPackageJson(string filePath, string entryPoint) {
}

var tree = Modules.GetModuleTree(Path.GetDirectoryName(filePath));

tree.DefaultPackage = entryPoint;

return new TreeUpdateAnalysis(tree);
tree.DefaultPackage = entryPoint;

var requireAnalysisUnits = new List<RequireAnalysisUnit>();
if (dependencies != null) {
var projectEntry = new ProjectEntry(this, filePath, null);
requireAnalysisUnits.AddRange(dependencies.Select(
dependency => { return new RequireAnalysisUnit(tree, Modules, projectEntry, dependency);
}));
}

return new TreeUpdateAnalysis(tree, requireAnalysisUnits);
}

/// <summary>
Expand All @@ -154,14 +162,22 @@ public IAnalyzable AddPackageJson(string filePath, string entryPoint) {
[Serializable]
internal class TreeUpdateAnalysis : IAnalyzable {
private readonly ModuleTree _tree;
public TreeUpdateAnalysis(ModuleTree tree) {
private readonly IEnumerable<RequireAnalysisUnit> _requireAnalysisUnits;

public TreeUpdateAnalysis(ModuleTree tree, IEnumerable<RequireAnalysisUnit> requireAnalysisUnits = null) {
_tree = tree;
_requireAnalysisUnits = requireAnalysisUnits;
}

public void Analyze(CancellationToken cancel) {
if (_tree != null) {
_tree.EnqueueDependents();
}
if (_requireAnalysisUnits != null) {
foreach (var unit in _requireAnalysisUnits) {
unit.AnalyzeWorker(null, cancel);
}
}
}
}

Expand Down
17 changes: 3 additions & 14 deletions Nodejs/Product/Analysis/Analysis/ModuleAnalysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ internal IEnumerable<MemberResult> GetModules() {
do {
ModuleTree nodeModules;
if (parentMT.Children.TryGetValue(AnalysisConstants.NodeModulesFolder, out nodeModules)) {
foreach (var child in GetChildrenExcludingNodeModules(nodeModules)) {
foreach (var child in nodeModules.GetChildrenExcludingNodeModules()) {
var module = ModuleTable.ResolveModule(child.Parent, child.Name);
if (module != null) {
res.Add(MakeProjectMemberResult(module.Name));
Expand All @@ -709,7 +709,7 @@ internal IEnumerable<MemberResult> GetModules() {
parentMT = parentMT.Parent;
} while (parentMT != null);

foreach (var sibling in GetChildrenExcludingNodeModules(moduleTree.Parent)) {
foreach (var sibling in moduleTree.Parent.GetChildrenExcludingNodeModules()) {
if (sibling == moduleTree) {
//Don't let us require ourself
continue;
Expand All @@ -731,19 +731,8 @@ private MemberResult MakeProjectMemberResult(string module) {
JsMemberType.Module);
}

private IEnumerable<ModuleTree> GetChildrenExcludingNodeModules(ModuleTree moduleTree) {
if (moduleTree == null) {
return Enumerable.Empty<ModuleTree>();
}
//Children.Values returns an IEnumerable
// The process of resolving modules can lead us to add entries into the underlying array
// doing so results in exceptions b/c the array has changed under the enumerable
// To avoid this, we call .ToArray() to create a copy of the array locally which we then Enumerate
return moduleTree.Children.Values.ToArray().Where(mod => !String.Equals(mod.Name, AnalysisConstants.NodeModulesFolder, StringComparison.OrdinalIgnoreCase));
}

private void GetChildModules(List<MemberResult> res, ModuleTree moduleTree, string projectRelative) {
foreach (var child in GetChildrenExcludingNodeModules(moduleTree)) {
foreach (var child in moduleTree.GetChildrenExcludingNodeModules()) {
Debug.Assert(child.Name != AnalysisConstants.NodeModulesFolder);
if (child.ProjectEntry == null) {
GetChildModules(res, child, projectRelative + child.Name + "/");
Expand Down
29 changes: 15 additions & 14 deletions Nodejs/Product/Analysis/Analysis/ModuleTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public void AddModule(string name, ProjectEntry value) {
tree.ProjectEntry = value;

// Update visibility..
AddVisibility(tree, value, true);
AddVisibility(tree, value);

value._enqueueModuleDependencies = true;

Expand All @@ -141,7 +141,7 @@ public void AddModule(string name, ProjectEntry value) {
// We share hashsets of visible nodes. They're stored in the ModuleTree and when a
// new module is added we assign it's _visibleEntries field to the one shared by all
// of it's peers. We then update the relevant entries with the new values.
private void AddVisibility(ModuleTree tree, ProjectEntry newModule, bool recurse) {
internal void AddVisibility(ModuleTree tree, ProjectEntry newModule) {
// My peers can see my assignments/I can see my peers assignments. Update
// ourselves and our peers so we can see each others writes.
var curTree = tree;
Expand Down Expand Up @@ -202,28 +202,29 @@ public IAnalysisSet RequireModule(Node node, AnalysisUnit unit, string moduleNam
}

private IAnalysisSet RequireModule(Node node, AnalysisUnit unit, string moduleName, ModuleTree relativeTo) {
ModuleTree tree = RequireModule(unit, moduleName, relativeTo);
if (node != null) {
return GetExports(node, unit, tree);
}
return AnalysisSet.Empty;
}

internal ModuleTree RequireModule(AnalysisUnit unit, string moduleName, ModuleTree relativeTo) {
ModuleTree curTree = null;
if (moduleName.StartsWith("./") || moduleName.StartsWith("../")) {
// search relative to our declaring module.
return GetExports(
node,
unit,
ResolveModule(relativeTo, moduleName, unit)
);
curTree = ResolveModule(relativeTo, moduleName, unit);
} else {
// must be in node_modules, search in the current directory
// and up through our parents
do {
var nodeModules = relativeTo.GetChild(AnalysisConstants.NodeModulesFolder, unit);
var curTree = ResolveModule(nodeModules, moduleName, unit);

if (curTree != null) {
return GetExports(node, unit, curTree);
}
curTree = ResolveModule(nodeModules, moduleName, unit);

relativeTo = relativeTo.Parent;
} while (relativeTo != null);
} while (relativeTo != null && curTree == null);
}
return AnalysisSet.Empty;
return curTree;
}

public static ModuleTree ResolveModule(ModuleTree parentTree, string relativeName) {
Expand Down
34 changes: 34 additions & 0 deletions Nodejs/Product/Analysis/Analysis/ModuleTreeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//*********************************************************//
// 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.Collections.Generic;
using System.Linq;

namespace Microsoft.NodejsTools.Analysis {
internal static class ModuleTreeExtensions {
internal static IEnumerable<ModuleTree> GetChildrenExcludingNodeModules(this ModuleTree moduleTree) {
if (moduleTree == null) {
return Enumerable.Empty<ModuleTree>();
}
// Children.Values returns an IEnumerable
// The process of resolving modules can lead us to add entries into the underlying array
// doing so results in exceptions b/c the array has changed under the enumerable
// To avoid this, we call .ToArray() to create a copy of the array locally which we then Enumerate
return moduleTree.Children.Values.ToArray().Where(mod => !String.Equals(mod.Name, AnalysisConstants.NodeModulesFolder, StringComparison.OrdinalIgnoreCase));
}
}
}
6 changes: 3 additions & 3 deletions Nodejs/Product/Nodejs/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ out project
internal static IComponentModel GetComponentModel(this IServiceProvider serviceProvider) {
return (IComponentModel)serviceProvider.GetService(typeof(SComponentModel));
}

internal static string GetFilePath(this ITextBuffer textBuffer) {
ITextDocument textDocument;
if (textBuffer.Properties.TryGetProperty<ITextDocument>(typeof(ITextDocument), out textDocument)) {
Expand Down Expand Up @@ -290,7 +290,7 @@ internal static void GotoSource(this LocationInfo location) {
location.FilePath,
location.Line - 1,
location.Column - 1
);
);
}

internal static SnapshotPoint? GetCaretPosition(this ITextView view) {
Expand Down Expand Up @@ -435,4 +435,4 @@ internal static ITrackingSpan GetApplicableSpan(this ITextSnapshot snapshot, int
return null;
}
}
}
}
26 changes: 20 additions & 6 deletions Nodejs/Product/Nodejs/Intellisense/VsProjectAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -374,24 +374,38 @@ public void AddPackageJson(string packageJsonPath) {

object mainFile;
if (json != null && json.TryGetValue("main", out mainFile) && mainFile is string) {
AddPackageJson(packageJsonPath, (string)mainFile);
List<string> dependencyList = GetDependencyListFromJson(json, "dependencies", "devDependencies", "optionalDependencies");
AddPackageJson(packageJsonPath, (string)mainFile, dependencyList);
}
}
}

public void AddPackageJson(string path, string mainFile) {
}

private static List<string> GetDependencyListFromJson(Dictionary<string, object> json, params string[] dependencyTypes) {
var allDependencies = new List<string>();
foreach (var type in dependencyTypes) {
object dependencies;
json.TryGetValue(type, out dependencies);
var dep = dependencies as Dictionary<string, object>;
if (dep != null) {
allDependencies.AddRange(dep.Keys.ToList());
}
}
return allDependencies;
}

public void AddPackageJson(string path, string mainFile, List<string> dependencies) {
if (!_fullyLoaded) {
lock (_loadingDeltas) {
if (!_fullyLoaded) {
_loadingDeltas.Add(() => AddPackageJson(path, mainFile));
_loadingDeltas.Add(() => AddPackageJson(path, mainFile, dependencies));
return;
}
}
}

if (ShouldEnqueue()) {
_analysisQueue.Enqueue(
_jsAnalyzer.AddPackageJson(path, mainFile),
_jsAnalyzer.AddPackageJson(path, mainFile, dependencies),
AnalysisPriority.Normal
);
}
Expand Down
1 change: 1 addition & 0 deletions Nodejs/Product/Nodejs/NodejsConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ internal class NodejsConstants {
internal const string StartWebBrowser = "StartWebBrowser";

internal const string NodeModulesFolder = "node_modules";
internal const string NodeModulesStagingFolder = "node_modules\\.staging\\";
internal const string AnalysisIgnoredDirectories = "AnalysisIgnoredDirectories";
internal const string AnalysisMaxFileSize = "AnalysisMaxFileSize";
internal const string DefaultIntellisenseCompletionCommittedBy = "{}[]().,:;+-*/%&|^!~=<>?@#'\"\\";
Expand Down
Loading

0 comments on commit 05b51f5

Please sign in to comment.