mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 05:19:39 +00:00
[npm] support serverless & cleanup
This commit is contained in:
parent
d2f06fbcec
commit
e32da59d30
36
extensions/npm/extension-browser.webpack.config.js
Normal file
36
extensions/npm/extension-browser.webpack.config.js
Normal 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;
|
|
@ -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'],
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
}
|
|
@ -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() {
|
|
@ -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> {
|
||||
|
|
|
@ -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==
|
||||
|
|
Loading…
Reference in a new issue