Skip to content

Commit 6100526

Browse files
authored
Merge pull request #1707 from paulvanbrenk/scopedPackages
Scoped packages
2 parents d0df204 + 6f0b5f5 commit 6100526

File tree

3 files changed

+113
-11
lines changed

3 files changed

+113
-11
lines changed

Nodejs/Product/Nodejs/NodejsConstants.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
1+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

33
using System;
44
using System.IO;
@@ -25,6 +25,7 @@ internal static class NodejsConstants
2525
internal const string ProjectFileFilter = "Node.js Project File (*.njsproj)\n*.njsproj\nAll Files (*.*)\n*.*\n";
2626

2727
internal const string NodeModulesFolder = "node_modules";
28+
internal const string NodeModulesFolderWithSeparators = "\\" + NodeModulesFolder + "\\";
2829
internal const string NodeModulesStagingFolder = "node_modules\\.staging\\";
2930
internal const string BowerComponentsFolder = "bower_components";
3031

Nodejs/Product/Nodejs/NpmUI/NpmWorker.cs

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
1+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

33
using System;
44
using System.Collections.Concurrent;
@@ -106,11 +106,23 @@ public async Task<IEnumerable<IPackage>> GetCatalogPackagesAsync(string filterTe
106106

107107
TelemetryHelper.LogSearchNpm();
108108

109-
var relativeUri = string.Format("/-/v1/search?text=\"{0}\"", WebUtility.UrlEncode(filterText));
110-
var searchUri = new Uri(defaultRegistryUri, relativeUri);
109+
if (filterText.Length == 1)
110+
{
111+
return await QueryNpmForSingleCharAsync(filterText);
112+
}
113+
else
114+
{
115+
return await QueryNpmAsync(filterText);
116+
}
117+
}
111118

112-
var request = WebRequest.Create(searchUri);
113-
using (var response = await request.GetResponseAsync())
119+
private async Task<IEnumerable<IPackage>> QueryNpmAsync(string filterText)
120+
{
121+
Debug.Assert(filterText.Length > 1, $"Use {nameof(QueryNpmForSingleCharAsync)} for single character queries.");
122+
123+
var relativeUri = $"/-/v1/search?text={WebUtility.UrlEncode(filterText)}";
124+
125+
using (var response = await QueryNpmRegistryAsync(relativeUri))
114126
{
115127
/* We expect the following response:
116128
{
@@ -160,7 +172,7 @@ public async Task<IEnumerable<IPackage>> GetCatalogPackagesAsync(string filterTe
160172
}
161173
*/
162174

163-
var reader = new StreamReader(response.GetResponseStream());
175+
using (var reader = new StreamReader(response.GetResponseStream()))
164176
using (var jsonReader = new JsonTextReader(reader))
165177
{
166178
while (jsonReader.Read())
@@ -183,6 +195,76 @@ public async Task<IEnumerable<IPackage>> GetCatalogPackagesAsync(string filterTe
183195
throw new InvalidOperationException("Unexpected json token.");
184196
}
185197

198+
private async Task<IEnumerable<IPackage>> QueryNpmForSingleCharAsync(string filterText)
199+
{
200+
Debug.Assert(filterText.Length == 1, $"Use {nameof(QueryNpmAsync)} for general queries when the search query has more than 1 character.");
201+
202+
// Special case since the search API won't return results for
203+
// single chararacter queries.
204+
var relativeUri = $"/{WebUtility.UrlEncode(filterText)}/latest";
205+
206+
using (var response = await QueryNpmRegistryAsync(relativeUri))
207+
{
208+
/* We expect the following response
209+
{
210+
"name": "express",
211+
"scope": "unscoped",
212+
"version": "4.15.2",
213+
"description": "Fast, unopinionated, minimalist web framework",
214+
"keywords": [ "express", "framework", "sinatra", "web", "rest", "restful", "router", "app", "api" ],
215+
"date": "2017-03-06T13:42:44.853Z",
216+
"links": {
217+
"npm": "https://www.npmjs.com/package/express",
218+
"homepage": "http://expressjs.com/",
219+
"repository": "https://github.com/expressjs/express",
220+
"bugs": "https://github.com/expressjs/express/issues"
221+
},
222+
"author": {
223+
"name": "TJ Holowaychuk",
224+
"email": "[email protected]"
225+
},
226+
"publisher": {
227+
"username": "dougwilson",
228+
"email": "[email protected]"
229+
},
230+
"maintainers": [
231+
{
232+
"username": "dougwilson",
233+
"email": "[email protected]"
234+
}
235+
]
236+
}*/
237+
using (var reader = new StreamReader(response.GetResponseStream()))
238+
using (var jsonReader = new JsonTextReader(reader))
239+
{
240+
while (jsonReader.Read())
241+
{
242+
if (jsonReader.TokenType == JsonToken.StartObject)
243+
{
244+
var token = JToken.ReadFrom(jsonReader);
245+
var package = ReadPackage(token, new NodeModuleBuilder());
246+
if (package != null)
247+
{
248+
return new[] { package };
249+
}
250+
}
251+
252+
throw new InvalidOperationException($"Unexpected json token. '{jsonReader.TokenType}'");
253+
}
254+
}
255+
}
256+
257+
throw new InvalidOperationException("Unexpected json token.");
258+
}
259+
260+
private static Task<WebResponse> QueryNpmRegistryAsync(string relativeUri)
261+
{
262+
var searchUri = new Uri(defaultRegistryUri, relativeUri);
263+
264+
var request = WebRequest.Create(searchUri);
265+
return request.GetResponseAsync();
266+
}
267+
186268
private IEnumerable<IPackage> ReadPackagesFromArray(JsonTextReader jsonReader)
187269
{
188270
var pkgList = new List<IPackage>();

Nodejs/Product/Npm/SPI/RootPackage.cs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
1+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3+
using System;
34
using System.Collections.Generic;
5+
using System.Diagnostics;
46
using System.Globalization;
57
using System.IO;
68
using System.Linq;
@@ -47,6 +49,23 @@ public RootPackage(
4749
{
4850
// otherwise we fail to create it completely...
4951
}
52+
53+
if (this.PackageJson != null)
54+
{
55+
this.Name = this.PackageJson.Name;
56+
}
57+
else
58+
{
59+
// this is the root package so the full folder name after node_nodules is the name
60+
// of the package
61+
var index = this.Path.IndexOf(NodejsConstants.NodeModulesFolderWithSeparators, StringComparison.OrdinalIgnoreCase);
62+
63+
Debug.Assert(index > -1, "Failed to find the node_modules folder.");
64+
65+
var name = this.Path.Substring(index + NodejsConstants.NodeModulesFolderWithSeparators.Length);
66+
67+
this.Name = name;
68+
}
5069
}
5170

5271
public IPackageJson PackageJson { get; }
@@ -55,13 +74,13 @@ public RootPackage(
5574

5675
public string Path { get; }
5776

77+
public string Name { get; }
78+
5879
public IEnumerable<string> Homepages => this.PackageJson?.Homepages ?? Enumerable.Empty<string>();
5980

6081
public bool HasPackageJson => this.PackageJson != null;
6182

62-
public string Name => this.PackageJson?.Name ?? new DirectoryInfo(this.Path).Name;
63-
64-
public SemverVersion Version => this.PackageJson?.Version ?? new SemverVersion();
83+
public SemverVersion Version => this.PackageJson?.Version ?? SemverVersion.UnknownVersion;
6584

6685
public IPerson Author => this.PackageJson?.Author;
6786

0 commit comments

Comments
 (0)