Skip to content

Commit 1f78795

Browse files
committed
enable running individual npm scripts in the containing folder
1 parent 739ae77 commit 1f78795

6 files changed

Lines changed: 80 additions & 6 deletions

File tree

extensions/npm/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,6 @@ The extension fetches data from https://registry.npmjs/org and https://registry.
3535
- `npm.enableScriptExplorer` - Enable an explorer view for npm scripts.
3636
- `npm.scriptExplorerAction` - The default click action: `open` or `run`, the default is `open`.
3737
- `npm.scriptCodeLens.enable` - Enable/disable the code lenses to run a script, the default is `false`.
38+
- `npm.enableRunFromFolderContextMenu` - Enable running npm scripts from the context menu of folders in Explorer, the default is `true`.
39+
3840

extensions/npm/package.json

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@
8888
{
8989
"command": "npm.runSelectedScript",
9090
"title": "%command.runSelectedScript%"
91+
},
92+
{
93+
"command": "npm.runScriptsFromFolder",
94+
"title": "%command.runScriptsFromFolder%"
9195
}
9296
],
9397
"menus": {
@@ -172,7 +176,14 @@
172176
"when": "view == npm && viewItem == script",
173177
"group": "navigation@3"
174178
}
175-
]
179+
],
180+
"explorer/context": [
181+
{
182+
"when": "config.npm.enableRunFromFolderContextMenu && explorerViewletVisible && explorerResourceIsFolder",
183+
"command": "npm.runScriptsFromFolder",
184+
"group": "2_workspace"
185+
}
186+
]
176187
},
177188
"configuration": {
178189
"id": "npm",
@@ -222,6 +233,12 @@
222233
"scope": "resource",
223234
"description": "%config.npm.enableScriptExplorer%"
224235
},
236+
"npm.enableRunFromFolderContextMenu": {
237+
"type": "boolean",
238+
"default": true,
239+
"scope": "resource",
240+
"description": "%config.npm.enableRunFromFolderContextMenu%"
241+
},
225242
"npm.scriptExplorerAction": {
226243
"type": "string",
227244
"enum": [

extensions/npm/package.nls.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"config.npm.enableScriptExplorer": "Enable an explorer view for npm scripts.",
99
"config.npm.scriptExplorerAction": "The default click action used in the scripts explorer: `open` or `run`, the default is `open`.",
1010
"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.",
11+
"config.npm.enableRunFromFolderContextMenu": "Enable running scripts from the context menu of a folder in Explorer",
1112
"npm.parseError": "Npm task detection: failed to parse the file {0}",
1213
"taskdef.script": "The npm script to customize.",
1314
"taskdef.path": "The path to the folder of the package.json file that provides the script. Can be omitted.",
@@ -17,5 +18,6 @@
1718
"command.debug": "Debug",
1819
"command.openScript": "Open",
1920
"command.runInstall": "Run Install",
20-
"command.runSelectedScript": "Run Script"
21+
"command.runSelectedScript": "Run Script",
22+
"command.runScriptsFromFolder": "Select npm Scripts to Run..."
2123
}

extensions/npm/src/commands.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import * as nls from 'vscode-nls';
67
import * as vscode from 'vscode';
8+
79
import {
8-
runScript, findScriptAtPosition
10+
detectNpmScriptsForFolder,
11+
findScriptAtPosition,
12+
getPackageJsonUriFromTask,
13+
runScript
914
} from './tasks';
10-
import * as nls from 'vscode-nls';
1115

1216
const localize = nls.loadMessageBundle();
1317

@@ -28,4 +32,29 @@ export function runSelectedScript() {
2832
let message = localize('noScriptFound', 'Could not find a valid npm script at the selection.');
2933
vscode.window.showErrorMessage(message);
3034
}
31-
}
35+
}
36+
37+
export async function selectAndRunScriptFromFolder(folderInfo: vscode.Uri) {
38+
type TaskMap = { [id: string]: vscode.Task; };
39+
let taskList = await detectNpmScriptsForFolder(folderInfo.path);
40+
41+
if (taskList && taskList.length > 0) {
42+
let taskMap: TaskMap = {};
43+
taskList.forEach(t => {
44+
let uri = getPackageJsonUriFromTask(t);
45+
if (uri && uri.fsPath.length >= folderInfo.fsPath.length) {
46+
let taskName = uri.fsPath.substring(folderInfo.fsPath.length, uri.fsPath.length - '/package.json'.length) + ' > ' + t.name.substring(0, t.name.search('-'));
47+
taskMap[taskName] = t;
48+
}
49+
});
50+
await vscode.window.showQuickPick(Object.keys(taskMap).sort(), {
51+
placeHolder: `Run scripts on folder ${folderInfo.fsPath}...`,
52+
onDidSelectItem: (taskToRun: string) => {
53+
vscode.tasks.executeTask(taskMap[taskToRun]);
54+
}
55+
});
56+
}
57+
else {
58+
vscode.window.showInformationMessage(`No scripts detected in folder ${folderInfo.path}`);
59+
}
60+
}

extensions/npm/src/main.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
import * as httpRequest from 'request-light';
77
import * as vscode from 'vscode';
88
import { addJSONProviders } from './features/jsonContributions';
9+
import { runSelectedScript, selectAndRunScriptFromFolder } from './commands';
910
import { NpmScriptsTreeDataProvider } from './npmView';
1011
import { invalidateTasksCache, NpmTaskProvider, hasPackageJson } from './tasks';
1112
import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHover';
12-
import { runSelectedScript } from './commands';
1313

1414
let treeDataProvider: NpmScriptsTreeDataProvider | undefined;
1515

@@ -45,6 +45,8 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
4545
if (await hasPackageJson()) {
4646
vscode.commands.executeCommand('setContext', 'npm:showScriptExplorer', true);
4747
}
48+
49+
context.subscriptions.push(vscode.commands.registerCommand('npm.runScriptsFromFolder', selectAndRunScriptFromFolder));
4850
}
4951

5052
function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposable | undefined {

extensions/npm/src/tasks.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,28 @@ async function detectNpmScripts(): Promise<Task[]> {
155155
}
156156
}
157157

158+
159+
export async function detectNpmScriptsForFolder(folder: string): Promise<Task[]> {
160+
161+
let allTasks: Task[] = [];
162+
let visitedPackageJsonFiles: Set<string> = new Set();
163+
164+
try {
165+
let relativePattern = new RelativePattern(folder, '**/package.json');
166+
let paths = await workspace.findFiles(relativePattern, '**/node_modules/**');
167+
for (const path of paths) {
168+
if (!visitedPackageJsonFiles.has(path.fsPath)) {
169+
let tasks = await provideNpmScriptsForFolder(path);
170+
visitedPackageJsonFiles.add(path.fsPath);
171+
allTasks.push(...tasks);
172+
}
173+
}
174+
return allTasks;
175+
} catch (error) {
176+
return Promise.reject(error);
177+
}
178+
}
179+
158180
export async function provideNpmScripts(): Promise<Task[]> {
159181
if (!cachedTasks) {
160182
cachedTasks = await detectNpmScripts();

0 commit comments

Comments
 (0)