add Debug script command

This commit is contained in:
Erich Gamma 2018-03-30 16:51:05 +02:00
parent a765cafa61
commit 2ce1f53e36
4 changed files with 167 additions and 108 deletions

View file

@ -44,11 +44,15 @@
"commands": [
{
"command": "npm.runScript",
"title": "Run Script"
"title": "Run"
},
{
"command": "npm.debugScript",
"title": "Debug"
},
{
"command": "npm.openScript",
"title": "Open Script"
"title": "Open"
},
{
"command": "npm.refresh",
@ -72,6 +76,16 @@
"command": "npm.openScript",
"when": "view == npm && viewItem == packageJSON",
"group": "1_navigation"
},
{
"command": "npm.runScript",
"when": "view == npm && viewItem == script",
"group": "1_navigation"
},
{
"command": "npm.debugScript",
"when": "view == npm && viewItem == script",
"group": "1_navigation"
}
]
},

View file

@ -5,7 +5,6 @@
'use strict';
import * as path from 'path';
import * as fs from 'fs';
import * as httpRequest from 'request-light';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
@ -15,40 +14,11 @@ const localize = nls.loadMessageBundle();
import { addJSONProviders } from './features/jsonContributions';
import { NpmScriptsTreeDataProvider } from './npmView';
import { NpmTaskDefinition, ScriptValidator, isWorkspaceFolder } from './tasks';
import { NpmTaskDefinition, getScripts } from './tasks';
type AutoDetect = 'on' | 'off';
let taskProvider: vscode.Disposable | undefined;
class Validator implements ScriptValidator {
async scriptIsValid(task: vscode.Task): Promise<boolean> {
let uri: vscode.Uri | null = this.getPackageJsonUri(task);
if (uri) {
let tasks = await provideNpmScriptsForFolder(uri);
for (let i = 0; i < tasks.length; i++) {
const t = tasks[i];
if (isWorkspaceFolder(task.scope) && isWorkspaceFolder(task.scope)) {
if (t.name === task.name && t.scope === task.scope && (<vscode.ShellExecution>(t.execution)).commandLine === (<vscode.ShellExecution>(task.execution)).commandLine) {
return true;
}
}
}
}
return false;
}
getPackageJsonUri(task: vscode.Task): vscode.Uri | null {
if (isWorkspaceFolder(task.scope)) {
if (task.definition.path) {
return vscode.Uri.file(path.join(task.scope.uri.fsPath, task.definition.path, 'package.json'));
} else {
return vscode.Uri.file(path.join(task.scope.uri.fsPath, 'package.json'));
}
}
return null;
}
}
export function activate(context: vscode.ExtensionContext): void {
let provider: vscode.TaskProvider = {
provideTasks: () => {
@ -60,7 +30,7 @@ export function activate(context: vscode.ExtensionContext): void {
};
taskProvider = vscode.workspace.registerTaskProvider('npm', provider);
vscode.window.registerTreeDataProvider('npm', new NpmScriptsTreeDataProvider(context, provider, new Validator()));
vscode.window.registerTreeDataProvider('npm', new NpmScriptsTreeDataProvider(context, provider, localize));
if (!vscode.workspace.workspaceFolders) {
return;
@ -83,25 +53,6 @@ export function deactivate(): void {
}
}
async function exists(file: string): Promise<boolean> {
return new Promise<boolean>((resolve, _reject) => {
fs.exists(file, (value) => {
resolve(value);
});
});
}
async function readFile(file: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
fs.readFile(file, (err, data) => {
if (err) {
reject(err);
}
resolve(data.toString());
});
});
}
const buildNames: string[] = ['build', 'compile', 'watch'];
function isBuildTask(name: string): boolean {
for (let buildName of buildNames) {
@ -182,46 +133,29 @@ function isExcluded(folder: vscode.WorkspaceFolder, packageJsonUri: vscode.Uri)
async function provideNpmScriptsForFolder(packageJsonUri: vscode.Uri): Promise<vscode.Task[]> {
let emptyTasks: vscode.Task[] = [];
if (packageJsonUri.scheme !== 'file') {
return emptyTasks;
}
let packageJson = packageJsonUri.fsPath;
if (!await exists(packageJson)) {
return emptyTasks;
}
let folder = vscode.workspace.getWorkspaceFolder(packageJsonUri);
if (!folder) {
return emptyTasks;
}
try {
var contents = await readFile(packageJson);
var json = JSON.parse(contents);
if (!json.scripts) {
return emptyTasks;
}
const result: vscode.Task[] = [];
Object.keys(json.scripts).filter(isNotPreOrPostScript).forEach(each => {
const task = createTask(each, `run ${each}`, folder!, packageJsonUri);
const lowerCaseTaskName = each.toLowerCase();
if (isBuildTask(lowerCaseTaskName)) {
task.group = vscode.TaskGroup.Build;
} else if (isTestTask(lowerCaseTaskName)) {
task.group = vscode.TaskGroup.Test;
}
result.push(task);
});
// always add npm install (without a problem matcher)
// result.push(createTask('install', 'install', rootPath, folder, []));
return result;
} catch (e) {
let localizedParseError = localize('npm.parseError', 'Npm task detection: failed to parse the file {0}', packageJsonUri);
throw new Error(localizedParseError);
let scripts = await getScripts(packageJsonUri, localize);
if (!scripts) {
return emptyTasks;
}
const result: vscode.Task[] = [];
Object.keys(scripts).filter(isNotPreOrPostScript).forEach(each => {
const task = createTask(each, `run ${each}`, folder!, packageJsonUri);
const lowerCaseTaskName = each.toLowerCase();
if (isBuildTask(lowerCaseTaskName)) {
task.group = vscode.TaskGroup.Build;
} else if (isTestTask(lowerCaseTaskName)) {
task.group = vscode.TaskGroup.Test;
}
result.push(task);
});
// always add npm install (without a problem matcher)
// result.push(createTask('install', 'install', rootPath, folder, []));
return result;
}
function createTask(script: string, cmd: string, folder: vscode.WorkspaceFolder, packageJsonUri: vscode.Uri, matcher?: any): vscode.Task {

View file

@ -2,14 +2,15 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import {
ExtensionContext, Task, TreeDataProvider, TreeItem, TreeItemCollapsibleState,
WorkspaceFolder, workspace, commands, window, EventEmitter, Event,
ThemeIcon, Uri, TextDocument, TaskProvider
} from 'vscode';
import { NpmTaskDefinition, ScriptValidator, isWorkspaceFolder } from './tasks';
import * as path from 'path';
import {
DebugConfiguration, Event, EventEmitter, ExtensionContext, Task, TaskProvider,
TextDocument, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri,
WorkspaceFolder, commands, debug, window, workspace
} from 'vscode';
import { NpmTaskDefinition, getPackageJsonUriFromTask, getScripts, isWorkspaceFolder } from './tasks';
class Folder extends TreeItem {
packages: PackageJSON[] = [];
@ -70,35 +71,95 @@ class NpmScript extends TreeItem {
this.command = {
title: 'Run Script',
command: 'npm.runScript',
arguments: [task]
arguments: [this]
};
}
}
export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> {
private taskTree: Folder[] | PackageJSON[] | null = null;
private validator: ScriptValidator;
private taskProvider: TaskProvider;
private localize: any;
private _onDidChangeTreeData: EventEmitter<TreeItem | null> = new EventEmitter<TreeItem | null>();
readonly onDidChangeTreeData: Event<TreeItem | null> = this._onDidChangeTreeData.event;
constructor(context: ExtensionContext, taskProvider: TaskProvider, validator: ScriptValidator) {
constructor(context: ExtensionContext, taskProvider: TaskProvider, localize: any) {
const subscriptions = context.subscriptions;
this.validator = validator;
this.taskProvider = taskProvider;
this.localize = localize;
subscriptions.push(commands.registerCommand('npm.runScript', this.runScript, this));
subscriptions.push(commands.registerCommand('npm.debugScript', this.debugScript, this));
subscriptions.push(commands.registerCommand('npm.openScript', this.openScript, this));
subscriptions.push(commands.registerCommand('npm.refresh', this.refresh, this));
}
private async runScript(task: Task) {
if (!await this.validator.scriptIsValid(task)) {
window.showErrorMessage(`Could not find script '${task.name}' or the script has changed. Try to refresh the view.`);
private async scriptIsValid(scripts: any, task: Task): Promise<boolean> {
if (scripts[task.name]) {
return true;
}
return false;
}
private async runScript(script: NpmScript) {
let task = script.task;
let uri = getPackageJsonUriFromTask(task);
let scripts = await getScripts(uri!, this.localize);
if (!await this.scriptIsValid(scripts, task)) {
window.showErrorMessage(`Could not find script '${task.name}'. Try to refresh the view.`);
return;
}
workspace.executeTask(task);
workspace.executeTask(script.task);
}
private async extractPort(scripts: any, task: Task): Promise<number | null> {
let script: string = scripts[task.name];
let match = script.match(/--inspect-brk=(\d*)/);
if (match && match.length === 2) {
return parseInt(match[1]);
}
return null;
}
private async debugScript(script: NpmScript) {
let task = script.task;
let uri = getPackageJsonUriFromTask(task);
let scripts = await getScripts(uri!, this.localize);
if (!await this.scriptIsValid(scripts, task)) {
window.showErrorMessage(`Could not find script '${task.name}'. Try to refresh the view.`);
return;
}
let port = await this.extractPort(scripts, task);
// let debugArgs = null;
// if (!port) {
// port = 9229;
// debugArgs = ['--', '--nolazy', `--inspect-brk=${port}`];
// }
if (!port) {
window.showErrorMessage(`Could not launch for debugging, the script does not define --inspect-brk=port.`);
return;
}
const config: DebugConfiguration = {
type: 'node',
request: 'launch',
name: `Debug ${task.name}`,
runtimeExecutable: 'npm',
runtimeArgs: [
'run-script',
task.name,
],
port: port
};
// if (debugArgs) {
// config.runtimeArgs.push(...debugArgs);
// }
if (isWorkspaceFolder(task.scope)) {
debug.startDebugging(task.scope, config);
}
}
private async openScript(packageJSON: PackageJSON) {

View file

@ -2,18 +2,68 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TaskDefinition, Task, WorkspaceFolder } from 'vscode';
import { TaskDefinition, Task, WorkspaceFolder, Uri } from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
export interface NpmTaskDefinition extends TaskDefinition {
script: string;
path?: string;
}
export interface ScriptValidator {
scriptIsValid(task: Task): Promise<boolean>;
}
export function isWorkspaceFolder(value: any): value is WorkspaceFolder {
return value && typeof value !== 'number';
}
}
export function getPackageJsonUriFromTask(task: Task): Uri | null {
if (isWorkspaceFolder(task.scope)) {
if (task.definition.path) {
return Uri.file(path.join(task.scope.uri.fsPath, task.definition.path, 'package.json'));
} else {
return Uri.file(path.join(task.scope.uri.fsPath, 'package.json'));
}
}
return null;
}
export async function exists(file: string): Promise<boolean> {
return new Promise<boolean>((resolve, _reject) => {
fs.exists(file, (value) => {
resolve(value);
});
});
}
export async function readFile(file: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
fs.readFile(file, (err, data) => {
if (err) {
reject(err);
}
resolve(data.toString());
});
});
}
export async function getScripts(packageJsonUri: Uri, localize: any): Promise<any> {
if (packageJsonUri.scheme !== 'file') {
return null;
}
let packageJson = packageJsonUri.fsPath;
if (!await exists(packageJson)) {
return null;
}
try {
var contents = await readFile(packageJson);
var json = JSON.parse(contents);
return json.scripts;
} catch (e) {
let localizedParseError = localize('npm.parseError', 'Npm task detection: failed to parse the file {0}', packageJsonUri);
throw new Error(localizedParseError);
}
}