Fixing #31217 npm script not detected if the package.json is not in root folder

This commit is contained in:
Erich Gamma 2018-02-28 16:51:01 +01:00
parent 86d5aba450
commit a6dde81592
5 changed files with 100 additions and 16 deletions

View file

@ -11,3 +11,4 @@ To run scripts as tasks you use the `Tasks` menu.
- `npm.autoDetect` enable detecting scripts as tasks, the default is `on`.
- `npm.runSilent` run npm script with the `--silent` option, the default is `false`.
- `npm.packageManager` the package manager used to run the scripts: `npm` or `yarn`, the default is `npm`.
- `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 user '**/test/**.

View file

@ -17,10 +17,12 @@
},
"dependencies": {
"jsonc-parser": "^1.0.0",
"minimatch": "^3.0.4",
"request-light": "^0.2.2",
"vscode-nls": "^3.2.1"
},
"devDependencies": {
"@types/minimatch": "^3.0.3",
"@types/node": "7.0.43"
},
"main": "./out/main",
@ -59,6 +61,17 @@
],
"default": "npm",
"description": "%config.npm.packageManager%"
},
"npm.exclude": {
"type": [
"string",
"array"
],
"items": {
"type": "string"
},
"description": "%config.npm.exclude%",
"scope": "window"
}
}
},
@ -91,4 +104,4 @@
}
]
}
}
}

View file

@ -4,6 +4,7 @@
"config.npm.autoDetect": "Controls whether auto detection of npm scripts is on or off. Default is on.",
"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.",
"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 ommitted."

View file

@ -9,6 +9,8 @@ import * as fs from 'fs';
import * as httpRequest from 'request-light';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import * as minimatch from 'minimatch';
const localize = nls.loadMessageBundle();
import { addJSONProviders } from './features/jsonContributions';
@ -97,15 +99,17 @@ function isNotPreOrPostScript(script: string): boolean {
async function provideNpmScripts(): Promise<vscode.Task[]> {
let emptyTasks: vscode.Task[] = [];
let allTasks: vscode.Task[] = [];
let folders = vscode.workspace.workspaceFolders;
if (!folders) {
let paths = await vscode.workspace.findFiles('**/package.json', '**/node_modules/**');
if (paths.length === 0) {
return emptyTasks;
}
try {
for (let i = 0; i < folders.length; i++) {
if (isEnabled(folders[i])) {
let tasks = await provideNpmScriptsForFolder(folders[i]);
for (let i = 0; i < paths.length; i++) {
let folder = vscode.workspace.getWorkspaceFolder(paths[i]);
if (folder && isEnabled(folder) && !isExcluded(folder, paths[i])) {
let tasks = await provideNpmScriptsForFolder(paths[i]);
allTasks.push(...tasks);
}
}
@ -119,19 +123,45 @@ function isEnabled(folder: vscode.WorkspaceFolder): boolean {
return vscode.workspace.getConfiguration('npm', folder.uri).get<AutoDetect>('autoDetect') === 'on';
}
async function provideNpmScriptsForFolder(folder: vscode.WorkspaceFolder): Promise<vscode.Task[]> {
function isExcluded(folder: vscode.WorkspaceFolder, packageJsonUri: vscode.Uri) {
function testForExclusionPattern(path: string, pattern: string): boolean {
return minimatch(path, pattern, { dot: true });
}
let exclude = vscode.workspace.getConfiguration('npm', folder.uri).get<string | string[]>('exclude');
if (exclude) {
if (Array.isArray(exclude)) {
for (let pattern of exclude) {
if (testForExclusionPattern(packageJsonUri.fsPath, pattern)) {
return true;
}
}
} else if (testForExclusionPattern(packageJsonUri.fsPath, exclude)) {
return true;
}
}
return false;
}
async function provideNpmScriptsForFolder(packageJsonUri: vscode.Uri): Promise<vscode.Task[]> {
let emptyTasks: vscode.Task[] = [];
if (folder.uri.scheme !== 'file') {
if (packageJsonUri.scheme !== 'file') {
return emptyTasks;
}
let rootPath = folder.uri.fsPath;
let packageJson = path.join(rootPath, 'package.json');
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);
@ -141,7 +171,7 @@ async function provideNpmScriptsForFolder(folder: vscode.WorkspaceFolder): Promi
const result: vscode.Task[] = [];
Object.keys(json.scripts).filter(isNotPreOrPostScript).forEach(each => {
const task = createTask(each, `run ${each}`, rootPath, folder);
const task = createTask(each, `run ${each}`, folder!, packageJsonUri);
const lowerCaseTaskName = each.toLowerCase();
if (isBuildTask(lowerCaseTaskName)) {
task.group = vscode.TaskGroup.Build;
@ -154,14 +184,17 @@ async function provideNpmScriptsForFolder(folder: vscode.WorkspaceFolder): Promi
// 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}', packageJson);
let localizedParseError = localize('npm.parseError', 'Npm task detection: failed to parse the file {0}', packageJsonUri);
throw new Error(localizedParseError);
}
}
function createTask(script: string, cmd: string, rootPath: string, folder: vscode.WorkspaceFolder, matcher?: any): vscode.Task {
function createTask(script: string, cmd: string, folder: vscode.WorkspaceFolder, packageJsonUri: vscode.Uri, matcher?: any): vscode.Task {
function getTaskName(script: string) {
function getTaskName(script: string, file: string) {
if (file.length) {
return `${script} - ${file.substring(0, file.length - 1)}`;
}
return script;
}
@ -173,10 +206,21 @@ function createTask(script: string, cmd: string, rootPath: string, folder: vscod
return `${packageManager} ${cmd}`;
}
function getRelativePath(folder: vscode.WorkspaceFolder, packageJsonUri: vscode.Uri): string {
let rootUri = folder.uri;
let absolutePath = packageJsonUri.fsPath;
return absolutePath.substring(rootUri.fsPath.length + 1, absolutePath.length - 'package.json'.length);
}
let kind: NpmTaskDefinition = {
type: 'npm',
script: script
};
let taskName = getTaskName(script);
return new vscode.Task(kind, folder, taskName, 'npm', new vscode.ShellExecution(getCommandLine(folder, cmd), { cwd: rootPath }), matcher);
let relativePackageJson = getRelativePath(folder, packageJsonUri);
if (relativePackageJson.length) {
kind.file = getRelativePath(folder, packageJsonUri);
}
let taskName = getTaskName(script, relativePackageJson);
let cwd = path.dirname(packageJsonUri.fsPath);
return new vscode.Task(kind, folder, taskName, 'npm', new vscode.ShellExecution(getCommandLine(folder, cmd), { cwd: cwd }), matcher);
}

View file

@ -2,6 +2,10 @@
# yarn lockfile v1
"@types/minimatch@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
"@types/node@7.0.43":
version "7.0.43"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c"
@ -12,6 +16,21 @@ agent-base@4, agent-base@^4.1.0:
dependencies:
es6-promisify "^5.0.0"
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
debug@2:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@ -52,6 +71,12 @@ jsonc-parser@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-1.0.1.tgz#7f8f296414e6e7c4a33b9e4914fc8c47e4421675"
minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
dependencies:
brace-expansion "^1.1.7"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"