Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: discover npm scripts in nested workspace folders #1417

Merged
merged 1 commit into from
Oct 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This changelog records changes to stable releases since 1.50.2. "TBA" changes he
- feat: add automatic support for nested sourcemaps ([#1390](https://github.com/microsoft/vscode-js-debug/issues/1390))
- fix: breakpoints failing to set on paths with multibyte URL characters ([#1364](https://github.com/microsoft/vscode-js-debug/issues/1364))
- fix: properly handle UNC paths ([#1148](https://github.com/microsoft/vscode-js-debug/issues/1148))
- fix: discover npm scripts in nested workspace folders ([#1321](https://github.com/microsoft/vscode-js-debug/issues/1321))

## v1.72 (September 2022)

Expand Down
2 changes: 1 addition & 1 deletion src/ui/configuration/nodeDebugConfigurationProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class NodeDynamicDebugConfigurationProvider extends BaseConfigurationProv
return [openTerminal];
}

const scripts = await findScripts([folder.uri.fsPath], true);
const scripts = await findScripts([folder], true);
if (!scripts) {
return [openTerminal];
}
Expand Down
65 changes: 43 additions & 22 deletions src/ui/debugNpmScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ interface IScript {
command: string;
}

type ScriptPickItem = vscode.QuickPickItem & { script: IScript };
type ScriptPickItem = vscode.QuickPickItem & { script?: IScript };

/**
* Opens a quickpick and them subsequently debugs a configured npm script.
* @param inFolder - Optionally scopes lookups to the given workspace folder
*/
export async function debugNpmScript(inFolder?: string) {
export async function debugNpmScript(inFolder?: vscode.WorkspaceFolder | string) {
const scripts = await findScripts(inFolder ? [inFolder] : undefined);
if (!scripts) {
return; // cancelled
Expand All @@ -50,14 +50,28 @@ export async function debugNpmScript(inFolder?: string) {
const multiDir = scripts.some(s => s.directory !== scripts[0].directory);
const quickPick = vscode.window.createQuickPick<ScriptPickItem>();

quickPick.items = scripts.map(script => ({
script,
label: multiDir ? `${path.basename(script.directory)}: ${script.name}` : script.name,
description: script.command,
}));
let lastDir: string | undefined;
const items: ScriptPickItem[] = [];
for (const script of scripts) {
if (script.directory !== lastDir && multiDir) {
items.push({
label: path.basename(script.directory),
kind: vscode.QuickPickItemKind.Separator,
});
lastDir = script.directory;
}

items.push({
script,
label: script.name,
description: script.command,
});
}
quickPick.items = items;

quickPick.onDidAccept(async () => {
runScript(quickPick.selectedItems[0].script);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
runScript(quickPick.selectedItems[0].script!);
quickPick.dispose();
});

Expand All @@ -76,10 +90,10 @@ const updateEditCandidate = (existing: IEditCandidate, updated: IEditCandidate)
* Finds configured npm scripts in the workspace.
*/
export async function findScripts(
inFolders: string[] | undefined,
inFolders: (vscode.WorkspaceFolder | string)[] | undefined,
silent = false,
): Promise<IScript[] | undefined> {
const folders = inFolders ?? vscode.workspace.workspaceFolders?.map(f => f.uri.fsPath) ?? [];
const folders = inFolders ?? vscode.workspace.workspaceFolders ?? [];

// 1. If there are no open folders, show an error and abort.
if (!folders || folders.length === 0) {
Expand All @@ -95,32 +109,39 @@ export async function findScripts(
}

// Otherwise, go through all package.json's in the folder and pull all the npm scripts we find.
const candidates = folders.map(directory => path.join(directory, 'package.json'));
const candidates = (
await Promise.all(
folders.map(f =>
vscode.workspace.findFiles(
new vscode.RelativePattern(f, '**/package.json'),
// matches https://github.com/microsoft/vscode/blob/18f743d534ef3f528c5e81e82e695b87c60d2ebf/extensions/npm/src/tasks.ts#L189
'**/{node_modules,.vscode-test}/**',
),
),
)
).flat();

const scripts: IScript[] = [];

// editCandidate is the file we'll edit if we don't find any npm scripts.
// We 'narrow' this as we parse to files that look more like a package.json we want
let editCandidate: IEditCandidate = { path: candidates[0], score: 0 };
for (const packageJson of candidates) {
if (!fs.existsSync(packageJson)) {
continue;
}

let editCandidate: IEditCandidate = { path: candidates[0].fsPath, score: 0 };
for (const { fsPath } of new Set(candidates)) {
// update this now, because we know it exists
editCandidate = updateEditCandidate(editCandidate, {
path: packageJson,
path: fsPath,
score: 1,
});

let parsed: { scripts?: { [key: string]: string } };
try {
parsed = JSON.parse(await readfile(packageJson));
parsed = JSON.parse(await readfile(fsPath));
} catch (e) {
if (!silent) {
promptToOpen(
'showWarningMessage',
localize('debug.npm.parseError', 'Could not read {0}: {1}', packageJson, e.message),
packageJson,
localize('debug.npm.parseError', 'Could not read {0}: {1}', fsPath, e.message),
fsPath,
);
}
// set the candidate to 'undefined', since we already displayed an error
Expand All @@ -138,7 +159,7 @@ export async function findScripts(

for (const key of Object.keys(parsed.scripts)) {
scripts.push({
directory: path.dirname(packageJson),
directory: path.dirname(fsPath),
name: key,
command: parsed.scripts[key],
});
Expand Down