Skip to content

Commit 6a5991c

Browse files
authored
Add node as npm script runner (microsoft#236967)
* refactor: Separate `createScriptRunnerTask` and `createInstallationTask` from `createTask` * feat: Add `npm.scriptRunner` * feat: Add Node.js as script runner * refactor: Refactor `isPrePostScript` * refactor: Extract `get*Command` * fix: Typo * style: Remove no-op `catch`es * fix: `node --run` doesn't support `--silent` * refactor: Use `.map` in `escapeCommandLine` * chore: Remove TODO Upstream reviewer is ok with current state
1 parent 5a42ab4 commit 6a5991c

File tree

8 files changed

+225
-154
lines changed

8 files changed

+225
-154
lines changed

extensions/npm/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ The extension fetches data from <https://registry.npmjs.org> and <https://regist
3434

3535
- `npm.autoDetect` - Enable detecting scripts as tasks, the default is `on`.
3636
- `npm.runSilent` - Run npm script with the `--silent` option, the default is `false`.
37-
- `npm.packageManager` - The package manager used to run the scripts: `auto`, `npm`, `yarn`, `pnpm` or `bun`. The default is `auto`, which detects your package manager based on files in your workspace.
37+
- `npm.packageManager` - The package manager used to install dependencies: `auto`, `npm`, `yarn`, `pnpm` or `bun`. The default is `auto`, which detects your package manager based on files in your workspace.
38+
- `npm.scriptRunner` - The script runner used to run the scripts: `auto`, `npm`, `yarn`, `pnpm`, `bun` or `node`. The default is `auto`, which detects your script runner based on files in your workspace.
3839
- `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 '&ast;&ast;/test/&ast;&ast;'.
3940
- `npm.enableScriptExplorer` - Enable an explorer view for npm scripts.
4041
- `npm.scriptExplorerAction` - The default click action: `open` or `run`, the default is `open`.

extensions/npm/package.json

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,28 @@
251251
"default": "auto",
252252
"description": "%config.npm.packageManager%"
253253
},
254+
"npm.scriptRunner": {
255+
"scope": "resource",
256+
"type": "string",
257+
"enum": [
258+
"auto",
259+
"npm",
260+
"yarn",
261+
"pnpm",
262+
"bun",
263+
"node"
264+
],
265+
"enumDescriptions": [
266+
"%config.npm.scriptRunner.auto%",
267+
"%config.npm.scriptRunner.npm%",
268+
"%config.npm.scriptRunner.yarn%",
269+
"%config.npm.scriptRunner.pnpm%",
270+
"%config.npm.scriptRunner.bun%",
271+
"%config.npm.scriptRunner.node%"
272+
],
273+
"default": "auto",
274+
"description": "%config.npm.scriptRunner%"
275+
},
254276
"npm.exclude": {
255277
"type": [
256278
"string",
@@ -341,18 +363,18 @@
341363
}
342364
],
343365
"terminalQuickFixes": [
344-
{
345-
"id": "ms-vscode.npm-command",
346-
"commandLineMatcher": "npm",
347-
"commandExitResult": "error",
348-
"outputMatcher": {
349-
"anchor": "bottom",
350-
"length": 8,
351-
"lineMatcher": "Did you mean (?:this|one of these)\\?((?:\\n.+?npm .+ #.+)+)",
352-
"offset": 2
353-
}
366+
{
367+
"id": "ms-vscode.npm-command",
368+
"commandLineMatcher": "npm",
369+
"commandExitResult": "error",
370+
"outputMatcher": {
371+
"anchor": "bottom",
372+
"length": 8,
373+
"lineMatcher": "Did you mean (?:this|one of these)\\?((?:\\n.+?npm .+ #.+)+)",
374+
"offset": 2
354375
}
355-
]
376+
}
377+
]
356378
},
357379
"repository": {
358380
"type": "git",

extensions/npm/package.nls.json

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,19 @@
55
"virtualWorkspaces": "Functionality that requires running the 'npm' command is not available in virtual workspaces.",
66
"config.npm.autoDetect": "Controls whether npm scripts should be automatically detected.",
77
"config.npm.runSilent": "Run npm commands with the `--silent` option.",
8-
"config.npm.packageManager": "The package manager used to run scripts.",
9-
"config.npm.packageManager.npm": "Use npm as the package manager for running scripts.",
10-
"config.npm.packageManager.yarn": "Use yarn as the package manager for running scripts.",
11-
"config.npm.packageManager.pnpm": "Use pnpm as the package manager for running scripts.",
12-
"config.npm.packageManager.bun": "Use bun as the package manager for running scripts.",
13-
"config.npm.packageManager.auto": "Auto-detect which package manager to use for running scripts based on lock files and installed package managers.",
8+
"config.npm.packageManager": "The package manager used to install dependencies.",
9+
"config.npm.packageManager.npm": "Use npm as the package manager.",
10+
"config.npm.packageManager.yarn": "Use yarn as the package manager.",
11+
"config.npm.packageManager.pnpm": "Use pnpm as the package manager.",
12+
"config.npm.packageManager.bun": "Use bun as the package manager.",
13+
"config.npm.packageManager.auto": "Auto-detect which package manager to use based on lock files and installed package managers.",
14+
"config.npm.scriptRunner": "The script runner used to run scripts.",
15+
"config.npm.scriptRunner.npm": "Use npm as the script runner.",
16+
"config.npm.scriptRunner.yarn": "Use yarn as the script runner.",
17+
"config.npm.scriptRunner.pnpm": "Use pnpm as the script runner.",
18+
"config.npm.scriptRunner.bun": "Use bun as the script runner.",
19+
"config.npm.scriptRunner.node": "Use Node.js as the script runner.",
20+
"config.npm.scriptRunner.auto": "Auto-detect which script runner to use based on lock files and installed package managers.",
1421
"config.npm.exclude": "Configure glob patterns for folders that should be excluded from automatic script detection.",
1522
"config.npm.enableScriptExplorer": "Enable an explorer view for npm scripts when there is no top-level 'package.json' file.",
1623
"config.npm.scriptExplorerAction": "The default click action used in the NPM Scripts Explorer: `open` or `run`, the default is `open`.",

extensions/npm/src/npmMain.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as vscode from 'vscode';
88
import { addJSONProviders } from './features/jsonContributions';
99
import { runSelectedScript, selectAndRunScriptFromFolder } from './commands';
1010
import { NpmScriptsTreeDataProvider } from './npmView';
11-
import { getPackageManager, invalidateTasksCache, NpmTaskProvider, hasPackageJson } from './tasks';
11+
import { getScriptRunner, getPackageManager, invalidateTasksCache, NpmTaskProvider, hasPackageJson } from './tasks';
1212
import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHover';
1313
import { NpmScriptLensProvider } from './npmScriptLens';
1414
import which from 'which';
@@ -63,9 +63,15 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
6363
context.subscriptions.push(vscode.commands.registerCommand('npm.refresh', () => {
6464
invalidateScriptCaches();
6565
}));
66+
context.subscriptions.push(vscode.commands.registerCommand('npm.scriptRunner', (args) => {
67+
if (args instanceof vscode.Uri) {
68+
return getScriptRunner(args, context, true);
69+
}
70+
return '';
71+
}));
6672
context.subscriptions.push(vscode.commands.registerCommand('npm.packageManager', (args) => {
6773
if (args instanceof vscode.Uri) {
68-
return getPackageManager(context, args);
74+
return getPackageManager(args, context, true);
6975
}
7076
return '';
7177
}));

extensions/npm/src/npmScriptLens.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import {
1515
workspace,
1616
l10n
1717
} from 'vscode';
18-
import { findPreferredPM } from './preferred-pm';
1918
import { readScripts } from './readScripts';
19+
import { getRunScriptCommand } from './tasks';
2020

2121

2222
const enum Constants {
@@ -87,18 +87,20 @@ export class NpmScriptLensProvider implements CodeLensProvider, Disposable {
8787
}
8888

8989
if (this.lensLocation === 'all') {
90-
const packageManager = await findPreferredPM(Uri.joinPath(document.uri, '..').fsPath);
91-
return tokens.scripts.map(
92-
({ name, nameRange }) =>
93-
new CodeLens(
90+
const folder = Uri.joinPath(document.uri, '..');
91+
return Promise.all(tokens.scripts.map(
92+
async ({ name, nameRange }) => {
93+
const runScriptCommand = await getRunScriptCommand(name, folder);
94+
return new CodeLens(
9495
nameRange,
9596
{
9697
title,
9798
command: 'extension.js-debug.createDebuggerTerminal',
98-
arguments: [`${packageManager.name} run ${name}`, workspace.getWorkspaceFolder(document.uri), { cwd }],
99+
arguments: [runScriptCommand.join(' '), workspace.getWorkspaceFolder(document.uri), { cwd }],
99100
},
100-
),
101-
);
101+
);
102+
},
103+
));
102104
}
103105

104106
return [];

extensions/npm/src/npmView.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ import {
1313
} from 'vscode';
1414
import { readScripts } from './readScripts';
1515
import {
16-
createTask, getPackageManager, getTaskName, isAutoDetectionEnabled, isWorkspaceFolder, INpmTaskDefinition,
16+
createInstallationTask, getTaskName, isAutoDetectionEnabled, isWorkspaceFolder, INpmTaskDefinition,
1717
NpmTaskProvider,
1818
startDebugging,
19+
detectPackageManager,
1920
ITaskWithLocation,
2021
INSTALL_SCRIPT
2122
} from './tasks';
@@ -150,8 +151,8 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> {
150151
}
151152

152153
private async runScript(script: NpmScript) {
153-
// Call getPackageManager to trigger the multiple lock files warning.
154-
await getPackageManager(this.context, script.getFolder().uri);
154+
// Call detectPackageManager to trigger the multiple lock files warning.
155+
await detectPackageManager(script.getFolder().uri, this.context, true);
155156
tasks.executeTask(script.task);
156157
}
157158

@@ -181,7 +182,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> {
181182
if (!uri) {
182183
return;
183184
}
184-
const task = await createTask(await getPackageManager(this.context, selection.folder.workspaceFolder.uri, true), 'install', ['install'], selection.folder.workspaceFolder, uri, undefined, []);
185+
const task = await createInstallationTask(this.context, selection.folder.workspaceFolder, uri);
185186
tasks.executeTask(task);
186187
}
187188

extensions/npm/src/scriptHover.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import {
1212
} from 'vscode';
1313
import { INpmScriptInfo, readScripts } from './readScripts';
1414
import {
15-
createTask,
16-
getPackageManager, startDebugging
15+
createScriptRunnerTask,
16+
startDebugging
1717
} from './tasks';
1818

1919

@@ -114,7 +114,7 @@ export class NpmScriptHoverProvider implements HoverProvider {
114114
const documentUri = args.documentUri;
115115
const folder = workspace.getWorkspaceFolder(documentUri);
116116
if (folder) {
117-
const task = await createTask(await getPackageManager(this.context, folder.uri), script, ['run', script], folder, documentUri);
117+
const task = await createScriptRunnerTask(this.context, script, folder, documentUri);
118118
await tasks.executeTask(task);
119119
}
120120
}

0 commit comments

Comments
 (0)