diff --git a/extensions/npm/README.md b/extensions/npm/README.md index 94db8a807fe..f3707f85d32 100644 --- a/extensions/npm/README.md +++ b/extensions/npm/README.md @@ -22,9 +22,9 @@ The Npm Script Explorer shows the npm scripts found in your workspace. The explo The extension supports to run the selected script as a task when editing the `package.json`file. You can either run a script from the hover shown on a script or using the command `Run Selected Npm Script`. -### Run Scripts from a Folder in Explorer +### Run Scripts from a Folder in the Explorer -The extension supports running a script as a task from the Explorer. Right-click a folder in Explorer and select the `Run npm Script in Folder...` option to bring up a command palette listing the scripts that the folder contains. You can run the script by selecting from the options listed in the command palette. +The extension supports running a script as a task from a folder in the Explorer. The command `Run NPM Script in Folder...` shown in the Explorer context menu finds all scripts in `package.json` files that are contained in this folder. You can then select the script to be executed as a task from the resulting list. You enable this support with the `npm.runScriptFromFolder` which is `false` by default. ### Others @@ -38,7 +38,7 @@ The extension fetches data from https://registry.npmjs.org and https://registry. - `npm.exclude` - Glob patterns for folders that should be excluded from automatic script detection. The pattern is matched against the **absolute path** of the package.json. For example, to exclude all test folders use '**/test/**'. - `npm.enableScriptExplorer` - Enable an explorer view for npm scripts. - `npm.scriptExplorerAction` - The default click action: `open` or `run`, the default is `open`. -- `npm.enableRunFromFolderContextMenu` - Enable running npm scripts from the context menu of folders in Explorer, the default is `false`. +- `npm.enableRunFromFolder` - Enable running npm scripts from the context menu of folders in Explorer, the default is `false`. - `npm.scriptCodeLens.enable` - Enable/disable the code lenses to run a script, the default is `false`. diff --git a/extensions/npm/package.json b/extensions/npm/package.json index af47994a7ea..64782111ec5 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -183,7 +183,7 @@ ], "explorer/context": [ { - "when": "config.npm.enableRunFromFolderContextMenu && explorerViewletVisible && explorerResourceIsFolder", + "when": "config.npm.enableRunFromFolder && explorerViewletVisible && explorerResourceIsFolder", "command": "npm.runScriptFromFolder", "group": "2_workspace" } @@ -237,11 +237,11 @@ "scope": "resource", "description": "%config.npm.enableScriptExplorer%" }, - "npm.enableRunFromFolderContextMenu": { + "npm.enableRunFromFolder": { "type": "boolean", "default": false, "scope": "resource", - "description": "%config.npm.enableRunFromFolderContextMenu%" + "description": "%config.npm.enableRunFromFolder%" }, "npm.scriptExplorerAction": { "type": "string", diff --git a/extensions/npm/package.nls.json b/extensions/npm/package.nls.json index 3cca674fc6e..014010a8425 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -1,23 +1,23 @@ { "description": "Extension to add task support for npm scripts.", - "displayName": "Npm support for VS Code", + "displayName": "NPM support for VS Code", "config.npm.autoDetect": "Controls whether npm scripts should be automatically detected.", "config.npm.runSilent": "Run npm commands with the `--silent` option.", "config.npm.packageManager": "The package manager used to run scripts.", "config.npm.exclude": "Configure glob patterns for folders that should be excluded from automatic script detection.", "config.npm.enableScriptExplorer": "Enable an explorer view for npm scripts when there is no top-level 'package.json' file.", - "config.npm.scriptExplorerAction": "The default click action used in the scripts explorer: `open` or `run`, the default is `open`.", - "config.npm.enableRunFromFolderContextMenu": "Enable running scripts from the context menu of a folder in Explorer", + "config.npm.scriptExplorerAction": "The default click action used in the npm scripts explorer: `open` or `run`, the default is `open`.", + "config.npm.enableRunFromFolder": "Enable running NPM scripts contained in a folder from the Explorer context menu.", "config.npm.fetchOnlinePackageInfo": "Fetch data from https://registry.npmjs.org and https://registry.bower.io to provide auto-completion and information on hover features on npm dependencies.", "npm.parseError": "Npm task detection: failed to parse the file {0}", "taskdef.script": "The npm script to customize.", "taskdef.path": "The path to the folder of the package.json file that provides the script. Can be omitted.", - "view.name": "Npm Scripts", + "view.name": "NPM Scripts", "command.refresh": "Refresh", "command.run": "Run", "command.debug": "Debug", "command.openScript": "Open", "command.runInstall": "Run Install", "command.runSelectedScript": "Run Script", - "command.runScriptFromFolder": "Run npm Script in Folder..." + "command.runScriptFromFolder": "Run NPM Script in Folder..." } diff --git a/extensions/npm/src/commands.ts b/extensions/npm/src/commands.ts index e9a94e0763f..7ca92879f9b 100644 --- a/extensions/npm/src/commands.ts +++ b/extensions/npm/src/commands.ts @@ -9,7 +9,8 @@ import * as vscode from 'vscode'; import { detectNpmScriptsForFolder, findScriptAtPosition, - runScript + runScript, + FolderTaskItem } from './tasks'; const localize = nls.loadMessageBundle(); @@ -34,10 +35,29 @@ export function runSelectedScript() { } export async function selectAndRunScriptFromFolder(selectedFolder: vscode.Uri) { - let taskList: { label: string, task: vscode.Task }[] = await detectNpmScriptsForFolder(selectedFolder); + let taskList: FolderTaskItem[] = await detectNpmScriptsForFolder(selectedFolder); if (taskList && taskList.length > 0) { - let result = await vscode.window.showQuickPick(taskList, { placeHolder: 'Select script' }); + const quickPick = vscode.window.createQuickPick(); + quickPick.title = 'Run NPM script in Folder'; + quickPick.placeholder = 'Select an npm script'; + quickPick.items = taskList; + + const toDispose: vscode.Disposable[] = []; + + let pickPromise = new Promise((c) => { + toDispose.push(quickPick.onDidAccept(() => { + toDispose.forEach(d => d.dispose()); + c(quickPick.selectedItems[0]); + })); + toDispose.push(quickPick.onDidHide(() => { + toDispose.forEach(d => d.dispose()); + c(undefined); + })); + }); + quickPick.show(); + let result = await pickPromise; + quickPick.dispose(); if (result) { vscode.tasks.executeTask(result.task); } diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index f80e0b1526b..cc21ffafc8f 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -5,7 +5,7 @@ import { TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace, - DebugConfiguration, debug, TaskProvider, TextDocument, tasks, TaskScope + DebugConfiguration, debug, TaskProvider, TextDocument, tasks, TaskScope, QuickPickItem } from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; @@ -20,6 +20,11 @@ export interface NpmTaskDefinition extends TaskDefinition { path?: string; } +export interface FolderTaskItem extends QuickPickItem { + label: string; + task: Task; +} + type AutoDetect = 'on' | 'off'; let cachedTasks: Task[] | undefined = undefined; @@ -156,39 +161,22 @@ async function detectNpmScripts(): Promise { } -export async function detectNpmScriptsForFolder(folder: Uri): Promise<{ label: string, task: Task }[]> { +export async function detectNpmScriptsForFolder(folder: Uri): Promise { - let folderTasks: { label: string, task: Task }[] = []; + let folderTasks: FolderTaskItem[] = []; try { let relativePattern = new RelativePattern(folder.fsPath, '**/package.json'); let paths = await workspace.findFiles(relativePattern, '**/node_modules/**'); - if (cachedTasks) { - let workspaceFolder = workspace.getWorkspaceFolder(folder); - if (workspaceFolder) { - let rootUri = workspaceFolder.uri.path; - if (rootUri === folder.path) { - return cachedTasks.map(t => ({ label: t.name, task: t })); - } - - let relativePaths = paths.map(p => ' - ' + p.path.substring(rootUri.length + 1, p.path.length - '/package.json'.length)); - for (const relativePath of relativePaths) { - folderTasks.push(...cachedTasks.filter(t => t.name.endsWith(relativePath)).map(t => ({ label: t.name, task: t }))); - } + let visitedPackageJsonFiles: Set = new Set(); + for (const path of paths) { + if (!visitedPackageJsonFiles.has(path.fsPath)) { + let tasks = await provideNpmScriptsForFolder(path); + visitedPackageJsonFiles.add(path.fsPath); + folderTasks.push(...tasks.map(t => ({ label: t.name, task: t }))); } } - else { - let visitedPackageJsonFiles: Set = new Set(); - for (const path of paths) { - if (!visitedPackageJsonFiles.has(path.fsPath)) { - let tasks = await provideNpmScriptsForFolder(path); - visitedPackageJsonFiles.add(path.fsPath); - folderTasks.push(...tasks.map(t => ({ label: t.name, task: t }))); - } - } - } - return folderTasks; } catch (error) { return Promise.reject(error);