[json] "Fetch Online Package Info" feature doesn't work when npm is not present. Fixes #77066

This commit is contained in:
Martin Aeschlimann
2019-09-30 16:59:34 +02:00
parent 2de9a3629f
commit bae6f5f1a5
2 changed files with 84 additions and 56 deletions

View File

@@ -25,7 +25,7 @@ export interface IJSONContribution {
getDocumentSelector(): DocumentSelector; getDocumentSelector(): DocumentSelector;
getInfoContribution(fileName: string, location: Location): Thenable<MarkedString[] | null> | null; getInfoContribution(fileName: string, location: Location): Thenable<MarkedString[] | null> | null;
collectPropertySuggestions(fileName: string, location: Location, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Thenable<any> | null; collectPropertySuggestions(fileName: string, location: Location, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Thenable<any> | null;
collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Thenable<any> | null; collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Thenable<any>;
collectDefaultSuggestions(fileName: string, result: ISuggestionsCollector): Thenable<any>; collectDefaultSuggestions(fileName: string, result: ISuggestionsCollector): Thenable<any>;
resolveSuggestion?(item: CompletionItem): Thenable<CompletionItem | null> | null; resolveSuggestion?(item: CompletionItem): Thenable<CompletionItem | null> | null;
} }

View File

@@ -182,11 +182,7 @@ export class PackageJSONContribution implements IJSONContribution {
return Promise.resolve(null); return Promise.resolve(null);
} }
public collectValueSuggestions( public async collectValueSuggestions(_fileName: string, location: Location, result: ISuggestionsCollector): Promise<any> {
_fileName: string,
location: Location,
result: ISuggestionsCollector
): Thenable<any> | null {
if (!this.onlineEnabled()) { if (!this.onlineEnabled()) {
return null; return null;
} }
@@ -194,34 +190,30 @@ export class PackageJSONContribution implements IJSONContribution {
if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) { if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) {
const currentKey = location.path[location.path.length - 1]; const currentKey = location.path[location.path.length - 1];
if (typeof currentKey === 'string') { if (typeof currentKey === 'string') {
return this.npmView(currentKey).then(info => { const info = await this.fetchPackageInfo(currentKey);
const latest = info.distTagsLatest; if (info && info.distTagsLatest) {
if (latest) {
let name = JSON.stringify(latest); let name = JSON.stringify(info.distTagsLatest);
let proposal = new CompletionItem(name); let proposal = new CompletionItem(name);
proposal.kind = CompletionItemKind.Property; proposal.kind = CompletionItemKind.Property;
proposal.insertText = name; proposal.insertText = name;
proposal.documentation = localize('json.npm.latestversion', 'The currently latest version of the package'); proposal.documentation = localize('json.npm.latestversion', 'The currently latest version of the package');
result.add(proposal); result.add(proposal);
name = JSON.stringify('^' + latest); name = JSON.stringify('^' + info.distTagsLatest);
proposal = new CompletionItem(name); proposal = new CompletionItem(name);
proposal.kind = CompletionItemKind.Property; proposal.kind = CompletionItemKind.Property;
proposal.insertText = name; proposal.insertText = name;
proposal.documentation = localize('json.npm.majorversion', 'Matches the most recent major version (1.x.x)'); proposal.documentation = localize('json.npm.majorversion', 'Matches the most recent major version (1.x.x)');
result.add(proposal); result.add(proposal);
name = JSON.stringify('~' + latest); name = JSON.stringify('~' + info.distTagsLatest);
proposal = new CompletionItem(name); proposal = new CompletionItem(name);
proposal.kind = CompletionItemKind.Property; proposal.kind = CompletionItemKind.Property;
proposal.insertText = name; proposal.insertText = name;
proposal.documentation = localize('json.npm.minorversion', 'Matches the most recent minor version (1.2.x)'); proposal.documentation = localize('json.npm.minorversion', 'Matches the most recent minor version (1.2.x)');
result.add(proposal); result.add(proposal);
} }
return 0;
}, () => {
return 0;
});
} }
} }
return null; return null;
@@ -243,25 +235,33 @@ export class PackageJSONContribution implements IJSONContribution {
return null; return null;
} }
private getInfo(pack: string): Thenable<string[]> { private async getInfo(pack: string): Promise<string[]> {
return this.npmView(pack).then(info => { let info = await this.fetchPackageInfo(pack);
if (info) {
const result: string[] = []; const result: string[] = [];
result.push(info.description || ''); result.push(info.description || '');
result.push(info.distTagsLatest ? localize('json.npm.version.hover', 'Latest version: {0}', info.distTagsLatest) : ''); result.push(info.distTagsLatest ? localize('json.npm.version.hover', 'Latest version: {0}', info.distTagsLatest) : '');
result.push(info.homepage || ''); result.push(info.homepage || '');
return result; return result;
}, () => {
return [];
});
} }
private npmView(pack: string): Promise<ViewPackageInfo> { return [];
return new Promise((resolve, reject) => { }
private async fetchPackageInfo(pack: string): Promise<ViewPackageInfo | undefined> {
let info = await this.npmView(pack);
if (!info) {
info = await this.npmjsView(pack);
}
return info;
}
private npmView(pack: string): Promise<ViewPackageInfo | undefined> {
return new Promise((resolve, _reject) => {
const command = 'npm view --json ' + pack + ' description dist-tags.latest homepage'; const command = 'npm view --json ' + pack + ' description dist-tags.latest homepage';
cp.exec(command, (error, stdout) => { cp.exec(command, (error, stdout) => {
if (error) { if (!error) {
return reject();
}
try { try {
const content = JSON.parse(stdout); const content = JSON.parse(stdout);
resolve({ resolve({
@@ -269,13 +269,41 @@ export class PackageJSONContribution implements IJSONContribution {
distTagsLatest: content['dist-tags.latest'], distTagsLatest: content['dist-tags.latest'],
homepage: content['homepage'] homepage: content['homepage']
}); });
return;
} catch (e) { } catch (e) {
reject(); // ignore
} }
}
resolve(undefined);
}); });
}); });
} }
private async npmjsView(pack: string): Promise<ViewPackageInfo | undefined> {
const queryUrl = 'https://registry.npmjs.org/' + encodeURIComponent(pack).replace(/%40/g, '@');
try {
const success = await this.xhr({
url: queryUrl,
agent: USER_AGENT
});
const obj = JSON.parse(success.responseText);
if (obj) {
const latest = obj && obj['dist-tags'] && obj['dist-tags']['latest'];
if (latest) {
return {
description: obj.description || '',
distTagsLatest: latest,
homepage: obj.homepage || ''
};
}
}
}
catch (e) {
//ignore
}
return undefined;
}
public getInfoContribution(_fileName: string, location: Location): Thenable<MarkedString[] | null> | null { public getInfoContribution(_fileName: string, location: Location): Thenable<MarkedString[] | null> | null {
if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) { if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) {
const pack = location.path[location.path.length - 1]; const pack = location.path[location.path.length - 1];