[npm] support serverless & cleanup

This commit is contained in:
Martin Aeschlimann 2020-06-17 11:39:14 +02:00
parent d2f06fbcec
commit e32da59d30
10 changed files with 146 additions and 88 deletions

View file

@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../shared.webpack.config');
const path = require('path');
const clientConfig = withDefaults({
target: 'webworker',
context: __dirname,
entry: {
extension: './src/npmBrowserMain.ts'
},
output: {
filename: 'npmBrowserMain.js'
},
performance: {
hints: false
},
resolve: {
alias: {
'vscode-nls': path.resolve(__dirname, '../../build/polyfills/vscode-nls.js')
}
},
node: {
'child_process': 'empty'
}
});
clientConfig.module.rules[0].use.shift(); // remove nls loader
module.exports = clientConfig;

View file

@ -14,12 +14,10 @@ const withDefaults = require('../shared.webpack.config');
module.exports = withDefaults({
context: __dirname,
entry: {
extension: './src/main.ts',
extension: './src/npmMain.ts',
},
output: {
filename: 'main.js',
path: path.join(__dirname, 'dist'),
libraryTarget: 'commonjs',
filename: 'npmMain.js',
},
resolve: {
mainFields: ['module', 'main'],

View file

@ -20,14 +20,15 @@
"dependencies": {
"jsonc-parser": "^2.2.1",
"minimatch": "^3.0.4",
"request-light": "^0.2.5",
"request-light": "^0.4.0",
"vscode-nls": "^4.1.1"
},
"devDependencies": {
"@types/minimatch": "^3.0.3",
"@types/node": "^12.11.7"
},
"main": "./out/main",
"main": "./out/npmMain",
"browser": "./dist/npmBrowserMain",
"activationEvents": [
"onCommand:workbench.action.tasks.runTask",
"onCommand:npm.runScriptFromFolder",
@ -181,12 +182,12 @@
}
],
"explorer/context": [
{
"when": "config.npm.enableRunFromFolder && explorerViewletVisible && explorerResourceIsFolder",
{
"when": "config.npm.enableRunFromFolder && explorerViewletVisible && explorerResourceIsFolder",
"command": "npm.runScriptFromFolder",
"group": "2_workspace"
}
]
"group": "2_workspace"
}
]
},
"configuration": {
"id": "npm",

View file

@ -3,11 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace } from 'vscode';
import { MarkdownString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace } from 'vscode';
import { IJSONContribution, ISuggestionsCollector } from './jsonContributions';
import { XHRRequest } from 'request-light';
import { Location } from 'jsonc-parser';
import { textToMarkedString } from './markedTextUtil';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
@ -181,13 +180,15 @@ export class BowerJSONContribution implements IJSONContribution {
});
}
public getInfoContribution(_resource: string, location: Location): Thenable<MarkedString[] | null> | null {
public getInfoContribution(_resource: string, location: Location): Thenable<MarkdownString[] | null> | null {
if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']))) {
const pack = location.path[location.path.length - 1];
if (typeof pack === 'string') {
return this.getInfo(pack).then(documentation => {
if (documentation) {
return [textToMarkedString(documentation)];
const str = new MarkdownString();
str.appendText(documentation);
return [str];
}
return null;
});

View file

@ -30,8 +30,8 @@ export interface IJSONContribution {
resolveSuggestion?(item: CompletionItem): Thenable<CompletionItem | null> | null;
}
export function addJSONProviders(xhr: XHRRequest): Disposable {
const contributions = [new PackageJSONContribution(xhr), new BowerJSONContribution(xhr)];
export function addJSONProviders(xhr: XHRRequest, canRunNPM: boolean): Disposable {
const contributions = [new PackageJSONContribution(xhr, canRunNPM), new BowerJSONContribution(xhr)];
const subscriptions: Disposable[] = [];
contributions.forEach(contribution => {
const selector = contribution.getDocumentSelector();

View file

@ -3,11 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace } from 'vscode';
import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace, MarkdownString } from 'vscode';
import { IJSONContribution, ISuggestionsCollector } from './jsonContributions';
import { XHRRequest } from 'request-light';
import { Location } from 'jsonc-parser';
import { textToMarkedString } from './markedTextUtil';
import * as cp from 'child_process';
import * as nls from 'vscode-nls';
@ -28,14 +27,12 @@ export class PackageJSONContribution implements IJSONContribution {
'jsdom', 'stylus', 'when', 'readable-stream', 'aws-sdk', 'concat-stream', 'chai', 'Thenable', 'wrench'];
private knownScopes = ['@types', '@angular', '@babel', '@nuxtjs', '@vue', '@bazel'];
private xhr: XHRRequest;
public getDocumentSelector(): DocumentSelector {
return [{ language: 'json', scheme: '*', pattern: '**/package.json' }];
}
public constructor(xhr: XHRRequest) {
this.xhr = xhr;
public constructor(private xhr: XHRRequest, private canRunNPM: boolean) {
}
public collectDefaultSuggestions(_fileName: string, result: ISuggestionsCollector): Thenable<any> {
@ -191,23 +188,23 @@ export class PackageJSONContribution implements IJSONContribution {
const currentKey = location.path[location.path.length - 1];
if (typeof currentKey === 'string') {
const info = await this.fetchPackageInfo(currentKey);
if (info && info.distTagsLatest) {
if (info && info.version) {
let name = JSON.stringify(info.distTagsLatest);
let name = JSON.stringify(info.version);
let proposal = new CompletionItem(name);
proposal.kind = CompletionItemKind.Property;
proposal.insertText = name;
proposal.documentation = localize('json.npm.latestversion', 'The currently latest version of the package');
result.add(proposal);
name = JSON.stringify('^' + info.distTagsLatest);
name = JSON.stringify('^' + info.version);
proposal = new CompletionItem(name);
proposal.kind = CompletionItemKind.Property;
proposal.insertText = name;
proposal.documentation = localize('json.npm.majorversion', 'Matches the most recent major version (1.x.x)');
result.add(proposal);
name = JSON.stringify('~' + info.distTagsLatest);
name = JSON.stringify('~' + info.version);
proposal = new CompletionItem(name);
proposal.kind = CompletionItemKind.Property;
proposal.insertText = name;
@ -219,14 +216,27 @@ export class PackageJSONContribution implements IJSONContribution {
return null;
}
private getDocumentation(description: string | undefined, version: string | undefined, homepage: string | undefined): MarkdownString {
const str = new MarkdownString();
if (description) {
str.appendText(description);
}
if (version) {
str.appendText('\n\n');
str.appendText(localize('json.npm.version.hover', 'Latest version: {0}', version));
}
if (homepage) {
str.appendText('\n\n');
str.appendText(homepage);
}
return str;
}
public resolveSuggestion(item: CompletionItem): Thenable<CompletionItem | null> | null {
if (item.kind === CompletionItemKind.Property && item.documentation === '') {
return this.getInfo(item.label).then(infos => {
if (infos.length > 0) {
item.documentation = infos[0];
if (infos.length > 1) {
item.detail = infos[1];
}
if (item.kind === CompletionItemKind.Property && !item.documentation) {
return this.fetchPackageInfo(item.label).then(info => {
if (info) {
item.documentation = this.getDocumentation(info.description, info.version, info.homepage);
return item;
}
return null;
@ -235,21 +245,11 @@ export class PackageJSONContribution implements IJSONContribution {
return null;
}
private async getInfo(pack: string): Promise<string[]> {
let info = await this.fetchPackageInfo(pack);
if (info) {
const result: string[] = [];
result.push(info.description || '');
result.push(info.distTagsLatest ? localize('json.npm.version.hover', 'Latest version: {0}', info.distTagsLatest) : '');
result.push(info.homepage || '');
return result;
}
return [];
}
private async fetchPackageInfo(pack: string): Promise<ViewPackageInfo | undefined> {
let info = await this.npmView(pack);
let info: ViewPackageInfo | undefined;
if (this.canRunNPM) {
info = await this.npmView(pack);
}
if (!info) {
info = await this.npmjsView(pack);
}
@ -259,14 +259,14 @@ export class PackageJSONContribution implements IJSONContribution {
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 version';
cp.exec(command, (error, stdout) => {
if (!error) {
try {
const content = JSON.parse(stdout);
resolve({
description: content['description'],
distTagsLatest: content['dist-tags.latest'],
version: content['dist-tags.latest'] || content['version'],
homepage: content['homepage']
});
return;
@ -280,22 +280,20 @@ export class PackageJSONContribution implements IJSONContribution {
}
private async npmjsView(pack: string): Promise<ViewPackageInfo | undefined> {
const queryUrl = 'https://registry.npmjs.org/' + encodeURIComponent(pack).replace(/%40/g, '@');
const queryUrl = 'https://api.npms.io/v2/package/' + encodeURIComponent(pack);
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 || ''
};
}
const metadata = obj?.collected?.metadata;
if (metadata) {
return {
description: metadata.description || '',
version: metadata.version,
homepage: metadata.links?.homepage || ''
};
}
}
catch (e) {
@ -308,9 +306,9 @@ export class PackageJSONContribution implements IJSONContribution {
if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) {
const pack = location.path[location.path.length - 1];
if (typeof pack === 'string') {
return this.getInfo(pack).then(infos => {
if (infos.length) {
return [infos.map(textToMarkedString).join('\n\n')];
return this.fetchPackageInfo(pack).then(info => {
if (info) {
return [this.getDocumentation(info.description, info.version, info.homepage)];
}
return null;
});
@ -339,7 +337,7 @@ export class PackageJSONContribution implements IJSONContribution {
proposal.kind = CompletionItemKind.Property;
proposal.insertText = insertText;
proposal.filterText = JSON.stringify(name);
proposal.documentation = pack.description || '';
proposal.documentation = this.getDocumentation(pack.description, pack.version, pack?.links?.homepage);
collector.add(proposal);
}
}
@ -349,10 +347,11 @@ interface SearchPackageInfo {
name: string;
description?: string;
version?: string;
links?: { homepage?: string; };
}
interface ViewPackageInfo {
description: string;
distTagsLatest?: string;
version?: string;
homepage?: string;
}

View file

@ -3,8 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MarkedString } from 'vscode';
import * as httpRequest from 'request-light';
import * as vscode from 'vscode';
import { addJSONProviders } from './features/jsonContributions';
export function textToMarkedString(text: string): MarkedString {
return text.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&'); // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
}
export async function activate(context: vscode.ExtensionContext): Promise<void> {
context.subscriptions.push(addJSONProviders(httpRequest.xhr, false));
}
export function deactivate(): void {
}

View file

@ -14,13 +14,19 @@ import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHov
let treeDataProvider: NpmScriptsTreeDataProvider | undefined;
export async function activate(context: vscode.ExtensionContext): Promise<void> {
registerTaskProvider(context);
treeDataProvider = registerExplorer(context);
registerHoverProvider(context);
configureHttpRequest();
let d = vscode.workspace.onDidChangeConfiguration((e) => {
configureHttpRequest();
context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('http.proxy') || e.affectsConfiguration('http.proxyStrictSSL')) {
configureHttpRequest();
}
}));
const canRunNPM = canRunNpmInCurrentWorkspace();
context.subscriptions.push(addJSONProviders(httpRequest.xhr, canRunNPM));
treeDataProvider = registerExplorer(context);
context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('npm.exclude') || e.affectsConfiguration('npm.autoDetect')) {
invalidateTasksCache();
if (treeDataProvider) {
@ -32,15 +38,12 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
treeDataProvider.refresh();
}
}
});
context.subscriptions.push(d);
}));
registerTaskProvider(context);
registerHoverProvider(context);
d = vscode.workspace.onDidChangeTextDocument((e) => {
invalidateHoverScriptsCache(e.document);
});
context.subscriptions.push(d);
context.subscriptions.push(vscode.commands.registerCommand('npm.runSelectedScript', runSelectedScript));
context.subscriptions.push(addJSONProviders(httpRequest.xhr));
if (await hasPackageJson()) {
vscode.commands.executeCommand('setContext', 'npm:showScriptExplorer', true);
@ -49,6 +52,13 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
context.subscriptions.push(vscode.commands.registerCommand('npm.runScriptFromFolder', selectAndRunScriptFromFolder));
}
function canRunNpmInCurrentWorkspace() {
if (vscode.workspace.workspaceFolders) {
return vscode.workspace.workspaceFolders.some(f => f.uri.scheme === 'file');
}
return false;
}
function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposable | undefined {
function invalidateScriptCaches() {

View file

@ -32,6 +32,9 @@ export class NpmScriptHoverProvider implements HoverProvider {
constructor(context: ExtensionContext) {
context.subscriptions.push(commands.registerCommand('npm.runScriptFromHover', this.runScriptFromHover, this));
context.subscriptions.push(commands.registerCommand('npm.debugScriptFromHover', this.debugScriptFromHover, this));
context.subscriptions.push(workspace.onDidChangeTextDocument((e) => {
invalidateHoverScriptsCache(e.document);
}));
}
public provideHover(document: TextDocument, position: Position, _token: CancellationToken): ProviderResult<Hover> {

View file

@ -71,7 +71,7 @@ http-proxy-agent@^2.1.0:
agent-base "4"
debug "3.1.0"
https-proxy-agent@^2.2.3:
https-proxy-agent@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
@ -96,16 +96,21 @@ ms@2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
request-light@^0.2.5:
version "0.2.5"
resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.2.5.tgz#38a3da7b2e56f7af8cbba57e8a94930ee2380746"
integrity sha512-eBEh+GzJAftUnex6tcL6eV2JCifY0+sZMIUpUPOVXbs2nV5hla4ZMmO3icYKGuGVuQ2zHE9evh4OrRcH4iyYYw==
request-light@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.4.0.tgz#c6b91ef00b18cb0de75d2127e55b3a2c9f7f90f9"
integrity sha512-fimzjIVw506FBZLspTAXHdpvgvQebyjpNyLRd0e6drPPRq7gcrROeGWRyF81wLqFg5ijPgnOQbmfck5wdTqpSA==
dependencies:
http-proxy-agent "^2.1.0"
https-proxy-agent "^2.2.3"
vscode-nls "^4.1.1"
https-proxy-agent "^2.2.4"
vscode-nls "^4.1.2"
vscode-nls@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c"
integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A==
vscode-nls@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167"
integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==