diff --git a/extensions/npm/src/commands.ts b/extensions/npm/src/commands.ts index 7ca92879f9b..4f9131a44b4 100644 --- a/extensions/npm/src/commands.ts +++ b/extensions/npm/src/commands.ts @@ -15,7 +15,7 @@ import { const localize = nls.loadMessageBundle(); -export function runSelectedScript() { +export function runSelectedScript(context: vscode.ExtensionContext) { let editor = vscode.window.activeTextEditor; if (!editor) { return; @@ -27,15 +27,15 @@ export function runSelectedScript() { let script = findScriptAtPosition(contents, offset); if (script) { - runScript(script, document); + runScript(context, script, document); } else { let message = localize('noScriptFound', 'Could not find a valid npm script at the selection.'); vscode.window.showErrorMessage(message); } } -export async function selectAndRunScriptFromFolder(selectedFolder: vscode.Uri) { - let taskList: FolderTaskItem[] = await detectNpmScriptsForFolder(selectedFolder); +export async function selectAndRunScriptFromFolder(context: vscode.ExtensionContext, selectedFolder: vscode.Uri) { + let taskList: FolderTaskItem[] = await detectNpmScriptsForFolder(context, selectedFolder); if (taskList && taskList.length > 0) { const quickPick = vscode.window.createQuickPick(); diff --git a/extensions/npm/src/npmMain.ts b/extensions/npm/src/npmMain.ts index 91936da6257..568c5ea3d6f 100644 --- a/extensions/npm/src/npmMain.ts +++ b/extensions/npm/src/npmMain.ts @@ -58,7 +58,7 @@ export async function activate(context: vscode.ExtensionContext): Promise })); context.subscriptions.push(vscode.commands.registerCommand('npm.packageManager', (args) => { if (args instanceof vscode.Uri) { - return getPackageManager(args); + return getPackageManager(context, args); } return ''; })); @@ -83,7 +83,7 @@ function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposab let workspaceWatcher = vscode.workspace.onDidChangeWorkspaceFolders((_e) => invalidateScriptCaches()); context.subscriptions.push(workspaceWatcher); - taskProvider = new NpmTaskProvider(); + taskProvider = new NpmTaskProvider(context); let disposable = vscode.tasks.registerTaskProvider('npm', taskProvider); context.subscriptions.push(disposable); return disposable; diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index e7eec0e84fa..8617b7f7f8d 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -146,7 +146,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { } private async debugScript(script: NpmScript) { - startDebugging(script.task.definition.script, path.dirname(script.package.resourceUri!.fsPath), script.getFolder()); + startDebugging(this.extensionContext, script.task.definition.script, path.dirname(script.package.resourceUri!.fsPath), script.getFolder()); } private findScript(document: TextDocument, script?: NpmScript): number { @@ -190,7 +190,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { if (!uri) { return; } - let task = await createTask('install', 'install', selection.folder.workspaceFolder, uri, undefined, []); + let task = await createTask(this.extensionContext, 'install', 'install', selection.folder.workspaceFolder, uri, undefined, []); tasks.executeTask(task); } diff --git a/extensions/npm/src/scriptHover.ts b/extensions/npm/src/scriptHover.ts index 01c0c4b8c63..48482660f52 100644 --- a/extensions/npm/src/scriptHover.ts +++ b/extensions/npm/src/scriptHover.ts @@ -30,7 +30,7 @@ export function invalidateHoverScriptsCache(document?: TextDocument) { export class NpmScriptHoverProvider implements HoverProvider { - constructor(context: ExtensionContext) { + constructor(private 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) => { @@ -103,7 +103,7 @@ export class NpmScriptHoverProvider implements HoverProvider { let documentUri = args.documentUri; let folder = workspace.getWorkspaceFolder(documentUri); if (folder) { - let task = await createTask(script, `run ${script}`, folder, documentUri); + let task = await createTask(this.context, script, `run ${script}`, folder, documentUri); await tasks.executeTask(task); } } @@ -113,7 +113,7 @@ export class NpmScriptHoverProvider implements HoverProvider { let documentUri = args.documentUri; let folder = workspace.getWorkspaceFolder(documentUri); if (folder) { - startDebugging(script, dirname(documentUri.fsPath), folder); + startDebugging(this.context, script, dirname(documentUri.fsPath), folder); } } } diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index 41e8cf45492..fac16a3960d 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, QuickPickItem, window, Position + DebugConfiguration, debug, TaskProvider, TextDocument, tasks, TaskScope, QuickPickItem, window, Position, ExtensionContext, env } from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; @@ -44,15 +44,15 @@ export interface TaskWithLocation { export class NpmTaskProvider implements TaskProvider { - constructor() { + constructor(private context: ExtensionContext) { } get tasksWithLocation(): Promise { - return provideNpmScripts(); + return provideNpmScripts(this.context); } public async provideTasks() { - const tasks = await provideNpmScripts(); + const tasks = await provideNpmScripts(this.context); return tasks.map(task => task.task); } @@ -70,7 +70,7 @@ export class NpmTaskProvider implements TaskProvider { } else { packageJsonUri = _task.scope.uri.with({ path: _task.scope.uri.path + '/package.json' }); } - return createTask(kind, `${kind.script === INSTALL_SCRIPT ? '' : 'run '}${kind.script}`, _task.scope, packageJsonUri); + return createTask(this.context, kind, `${kind.script === INSTALL_SCRIPT ? '' : 'run '}${kind.script}`, _task.scope, packageJsonUri); } return undefined; } @@ -123,16 +123,23 @@ export function isWorkspaceFolder(value: any): value is WorkspaceFolder { return value && typeof value !== 'number'; } -export async function getPackageManager(folder: Uri): Promise { +export async function getPackageManager(extensionContext: ExtensionContext, folder: Uri): Promise { let packageManagerName = workspace.getConfiguration('npm', folder).get('packageManager', 'npm'); if (packageManagerName === 'auto') { const { name, multiplePMDetected } = await findPreferredPM(folder.fsPath); packageManagerName = name; - - if (multiplePMDetected) { - const multiplePMWarning = localize('npm.multiplePMWarning', 'Found multiple lockfiles for {0}. Using {1} as the preferred package manager.', folder.fsPath, packageManagerName); - window.showWarningMessage(multiplePMWarning); + const neverShowWarning = 'npm.multiplePMWarning.neverShow'; + if (multiplePMDetected && !extensionContext.globalState.get(neverShowWarning)) { + const multiplePMWarning = localize('npm.multiplePMWarning', 'Using {0} as the preferred package manager. Found multiple lockfiles for {1}.', packageManagerName, folder.fsPath); + const neverShowAgain = localize('npm.multiplePMWarning.doNotShow', "Do not show again"); + const learnMore = localize('npm.multiplePMWarning.learnMore', "Learn more"); + window.showInformationMessage(multiplePMWarning, learnMore, neverShowAgain).then(result => { + switch (result) { + case neverShowAgain: extensionContext.globalState.update(neverShowWarning, true); break; + case learnMore: env.openExternal(Uri.parse('https://nodejs.dev/learn/the-package-lock-json-file')); + } + }); } } @@ -160,7 +167,7 @@ export async function hasNpmScripts(): Promise { } } -async function detectNpmScripts(): Promise { +async function detectNpmScripts(context: ExtensionContext): Promise { let emptyTasks: TaskWithLocation[] = []; let allTasks: TaskWithLocation[] = []; @@ -177,7 +184,7 @@ async function detectNpmScripts(): Promise { let paths = await workspace.findFiles(relativePattern, '**/{node_modules,.vscode-test}/**'); for (const path of paths) { if (!isExcluded(folder, path) && !visitedPackageJsonFiles.has(path.fsPath)) { - let tasks = await provideNpmScriptsForFolder(path); + let tasks = await provideNpmScriptsForFolder(context, path); visitedPackageJsonFiles.add(path.fsPath); allTasks.push(...tasks); } @@ -191,7 +198,7 @@ async function detectNpmScripts(): Promise { } -export async function detectNpmScriptsForFolder(folder: Uri): Promise { +export async function detectNpmScriptsForFolder(context: ExtensionContext, folder: Uri): Promise { let folderTasks: FolderTaskItem[] = []; @@ -202,7 +209,7 @@ export async function detectNpmScriptsForFolder(folder: Uri): Promise = new Set(); for (const path of paths) { if (!visitedPackageJsonFiles.has(path.fsPath)) { - let tasks = await provideNpmScriptsForFolder(path); + let tasks = await provideNpmScriptsForFolder(context, path); visitedPackageJsonFiles.add(path.fsPath); folderTasks.push(...tasks.map(t => ({ label: t.task.name, task: t.task }))); } @@ -213,9 +220,9 @@ export async function detectNpmScriptsForFolder(folder: Uri): Promise { +export async function provideNpmScripts(context: ExtensionContext): Promise { if (!cachedTasks) { - cachedTasks = await detectNpmScripts(); + cachedTasks = await detectNpmScripts(context); } return cachedTasks; } @@ -251,7 +258,7 @@ function isDebugScript(script: string): boolean { return match !== null; } -async function provideNpmScriptsForFolder(packageJsonUri: Uri): Promise { +async function provideNpmScriptsForFolder(context: ExtensionContext, packageJsonUri: Uri): Promise { let emptyTasks: TaskWithLocation[] = []; let folder = workspace.getWorkspaceFolder(packageJsonUri); @@ -269,7 +276,7 @@ async function provideNpmScriptsForFolder(packageJsonUri: Uri): Promise { +export async function createTask(context: ExtensionContext, script: NpmTaskDefinition | string, cmd: string, folder: WorkspaceFolder, packageJsonUri: Uri, detail?: string, matcher?: any): Promise { let kind: NpmTaskDefinition; if (typeof script === 'string') { kind = { type: 'npm', script: script }; @@ -307,7 +314,7 @@ export async function createTask(script: NpmTaskDefinition | string, cmd: string kind = script; } - const packageManager = await getPackageManager(folder.uri); + const packageManager = await getPackageManager(context, folder.uri); async function getCommandLine(cmd: string): Promise { if (workspace.getConfiguration('npm', folder.uri).get('runSilent')) { return `${packageManager} --silent ${cmd}`; @@ -368,22 +375,22 @@ async function exists(file: string): Promise { }); } -export async function runScript(script: string, document: TextDocument) { +export async function runScript(context: ExtensionContext, script: string, document: TextDocument) { let uri = document.uri; let folder = workspace.getWorkspaceFolder(uri); if (folder) { - let task = await createTask(script, `run ${script}`, folder, uri); + let task = await createTask(context, script, `run ${script}`, folder, uri); tasks.executeTask(task); } } -export async function startDebugging(scriptName: string, cwd: string, folder: WorkspaceFolder) { +export async function startDebugging(context: ExtensionContext, scriptName: string, cwd: string, folder: WorkspaceFolder) { const config: DebugConfiguration = { type: 'pwa-node', request: 'launch', name: `Debug ${scriptName}`, cwd, - runtimeExecutable: await getPackageManager(folder.uri), + runtimeExecutable: await getPackageManager(context, folder.uri), runtimeArgs: [ 'run', scriptName,