Skip to content

Commit

Permalink
fix(error-handling): Give more feedback for common errors (#881)
Browse files Browse the repository at this point in the history
Place feedback in output channel rather than debug console
Telemetry for invalid angular.json parsing / node_modules caching
Error messaging for angular.json missing
Error message for node_modules missing
  • Loading branch information
mrmeku authored and jaysoo committed Nov 12, 2019
1 parent a161c32 commit e343fbd
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 115 deletions.
2 changes: 1 addition & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
{
"glob": "README.md",
"input": "./",
"output": "/assets/"
"output": "/"
},
{
"glob": "**/*",
Expand Down
35 changes: 22 additions & 13 deletions apps/vscode/src/app/ng-task/ng-task-provider.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import { readJsonFile } from '@angular-console/server';
import { readAndParseJson } from '@angular-console/server';
import { existsSync } from 'fs';
import { join } from 'path';
import {
ProviderResult,
Task,
TaskExecution,
TaskProvider,
window,
tasks,
TaskExecution
window
} from 'vscode';
import * as path from 'path';

import { getTelemetry } from '../telemetry';
import { NgTask } from './ng-task';
import {
AngularJson,
NamedProject,
NgTaskDefinition,
ProjectDef,
NamedProject,
Projects
} from './ng-task-definition';
import { getTelemetry } from '../telemetry';

export class NgTaskProvider implements TaskProvider {
private workspacePath?: string;
Expand Down Expand Up @@ -70,6 +71,16 @@ export class NgTaskProvider implements TaskProvider {
}

executeTask(definition: NgTaskDefinition) {
if (
!this.workspacePath ||
!existsSync(join(this.workspacePath, 'node_modules'))
) {
window.showErrorMessage(
'Could not execute task since node_modules directory is missing. Run npm install.'
);
return;
}

const isDryRun = definition.flags.includes('--dry-run');
if (isDryRun && this.currentDryRun) {
this.deferredDryRun = definition;
Expand All @@ -94,14 +105,12 @@ export class NgTaskProvider implements TaskProvider {
}

try {
const { projects } = readJsonFile('angular.json', this.workspacePath)
.json as AngularJson;
const { projects } = readAndParseJson(
join(this.workspacePath, 'angular.json')
).json as AngularJson;

return projects;
} catch {
window.showErrorMessage(
'Your angular.json file is invalid (see debug console)'
);
} catch (e) {
return {};
}
}
Expand All @@ -118,7 +127,7 @@ export class NgTaskProvider implements TaskProvider {
if (!this.workspacePath) return null;

const entry = this.getProjectEntries().find(([_, def]) =>
selectedPath.startsWith(path.join(this.workspacePath!, def.root))
selectedPath.startsWith(join(this.workspacePath!, def.root))
);

return entry ? { name: entry[0], ...entry[1] } : null;
Expand Down
10 changes: 10 additions & 0 deletions apps/vscode/src/app/output-channel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { OutputChannel, window } from 'vscode';

let _channel: OutputChannel;

export function getOutputChannel(): OutputChannel {
if (!_channel) {
_channel = window.createOutputChannel('Angular Console');
}
return _channel;
}
210 changes: 131 additions & 79 deletions apps/vscode/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { cacheJsonFiles, EXTENSIONS } from '@angular-console/server';
import {
cacheJsonFiles,
EXTENSIONS,
readAndParseJson
} from '@angular-console/server';
import { stream } from 'fast-glob';
import { existsSync, readFileSync } from 'fs';
import { existsSync } from 'fs';
import { dirname, join, parse } from 'path';
import {
commands,
Expand All @@ -16,14 +20,15 @@ import { AngularJsonTreeItem } from './app/angular-json-tree/angular-json-tree-i
import { AngularJsonTreeProvider } from './app/angular-json-tree/angular-json-tree-provider';
import { registerNgTaskCommands } from './app/ng-task/ng-task-commands';
import { NgTaskProvider } from './app/ng-task/ng-task-provider';
import { getTelemetry, initTelemetry } from './app/telemetry';
import { VSCodeStorage } from './app/vscode-storage';
import { revealWebViewPanel } from './app/webview';
import { WorkspaceTreeItem } from './app/workspace-tree/workspace-tree-item';
import {
LOCATE_YOUR_WORKSPACE,
WorkspaceTreeProvider
} from './app/workspace-tree/workspace-tree-provider';
import { initTelemetry } from './app/telemetry';
import { getOutputChannel } from './app/output-channel';

let workspaceTreeView: TreeView<WorkspaceTreeItem>;
let angularJsonTreeView: TreeView<AngularJsonTreeItem>;
Expand All @@ -35,81 +40,100 @@ let ngTaskProvider: NgTaskProvider;
let context: ExtensionContext;

export function activate(c: ExtensionContext) {
context = c;
currentWorkspaceTreeProvider = WorkspaceTreeProvider.create({
extensionPath: context.extensionPath
});
const store = VSCodeStorage.fromContext(context);
initTelemetry(context, store);

ngTaskProvider = new NgTaskProvider();
tasks.registerTaskProvider('ng', ngTaskProvider);

registerNgTaskCommands(context, ngTaskProvider);

workspaceTreeView = window.createTreeView('angularConsole', {
treeDataProvider: currentWorkspaceTreeProvider
}) as TreeView<WorkspaceTreeItem>;
context.subscriptions.push(workspaceTreeView);

angularJsonTreeProvider = new AngularJsonTreeProvider(
context,
ngTaskProvider
);

angularJsonTreeView = window.createTreeView('angularConsoleJson', {
treeDataProvider: angularJsonTreeProvider
}) as TreeView<AngularJsonTreeItem>;
context.subscriptions.push(angularJsonTreeView);

context.subscriptions.push(
commands.registerCommand(
'angularConsole.revealWebViewPanel',
async (workspaceTreeItem: WorkspaceTreeItem) => {
switch (workspaceTreeItem.route) {
case 'Add':
const extensions = Object.entries(EXTENSIONS).map(
([label, description]): QuickPickItem => ({
label,
description
})
try {
context = c;
currentWorkspaceTreeProvider = WorkspaceTreeProvider.create({
extensionPath: context.extensionPath
});
const store = VSCodeStorage.fromContext(context);
initTelemetry(context, store);

ngTaskProvider = new NgTaskProvider();
tasks.registerTaskProvider('ng', ngTaskProvider);

registerNgTaskCommands(context, ngTaskProvider);

workspaceTreeView = window.createTreeView('angularConsole', {
treeDataProvider: currentWorkspaceTreeProvider
}) as TreeView<WorkspaceTreeItem>;
context.subscriptions.push(workspaceTreeView);

angularJsonTreeProvider = new AngularJsonTreeProvider(
context,
ngTaskProvider
);

angularJsonTreeView = window.createTreeView('angularConsoleJson', {
treeDataProvider: angularJsonTreeProvider
}) as TreeView<AngularJsonTreeItem>;
context.subscriptions.push(angularJsonTreeView);

context.subscriptions.push(
commands.registerCommand(
'angularConsole.revealWebViewPanel',
async (workspaceTreeItem: WorkspaceTreeItem) => {
if (
!existsSync(join(workspaceTreeItem.workspacePath, 'node_modules'))
) {
window.showErrorMessage(
'Angular Console requires your workspace have a node_modules directory. Run npm install.'
);
window.showQuickPick(extensions).then(selection => {
if (!selection) {
return;
}

ngTaskProvider.executeTask({
command: 'add',
positional: selection.label,
flags: []
return;
}
switch (workspaceTreeItem.route) {
case 'Add':
const extensions = Object.entries(EXTENSIONS).map(
([label, description]): QuickPickItem => ({
label,
description
})
);
window.showQuickPick(extensions).then(selection => {
if (!selection) {
return;
}

ngTaskProvider.executeTask({
command: 'add',
positional: selection.label,
flags: []
});
});
});
}

revealWebViewPanel({
workspaceTreeItem,
context,
ngTaskProvider,
workspaceTreeView
});
}
)
);
context.subscriptions.push(
commands.registerCommand(
LOCATE_YOUR_WORKSPACE.command!.command,
async () => {
return await locateAngularWorkspace();
}
)
);

revealWebViewPanel({
workspaceTreeItem,
context,
ngTaskProvider,
workspaceTreeView
});
}
)
);
context.subscriptions.push(
commands.registerCommand(
LOCATE_YOUR_WORKSPACE.command!.command,
async () => {
return await locateAngularWorkspace();
}
)
);
const vscodeWorkspacePath =
workspace.workspaceFolders && workspace.workspaceFolders[0].uri.fsPath;

const vscodeWorkspacePath =
workspace.workspaceFolders && workspace.workspaceFolders[0].uri.fsPath;
if (vscodeWorkspacePath) {
scanForWorkspace(vscodeWorkspacePath);
}
} catch (e) {
window.showErrorMessage(
'Angular Console encountered an error when activating (see output panel)'
);

if (vscodeWorkspacePath) {
scanForWorkspace(vscodeWorkspacePath);
getOutputChannel().appendLine(
'Angular Console encountered an error when activating'
);
getOutputChannel().appendLine(JSON.stringify(e));
}
}

Expand Down Expand Up @@ -161,18 +185,24 @@ function scanForWorkspace(vscodeWorkspacePath: string) {

async function setAngularWorkspace(workspacePath: string) {
try {
JSON.parse(readFileSync(join(workspacePath, 'angular.json')).toString());
readAndParseJson(join(workspacePath, 'angular.json'));
} catch (e) {
console.error('Invalid angular JSON', e);
window.showErrorMessage(
'Your angular.json file is invalid (see debug console)'
'Invalid angular.json (see output panel for details)'
);
commands.executeCommand('setContext', 'isAngularWorkspace', false);
return;
getOutputChannel().appendLine(
'Invalid angular JSON: ' + join(workspacePath, 'angular.json')
);

const stringifiedError = e.toString ? e.toString() : JSON.stringify(e);
getOutputChannel().appendLine(stringifiedError);
getTelemetry().exceptionOccured(stringifiedError);
}

cacheJsonFiles(workspacePath);
setInterval(() => cacheJsonFiles(workspacePath), 60000);
setTimeout(() => {
cacheWorkspaceNodeModulesJsons(workspacePath);
}, 0);
setInterval(() => cacheWorkspaceNodeModulesJsons(workspacePath), 60000);

commands.executeCommand('setContext', 'isAngularWorkspace', true);

Expand All @@ -182,3 +212,25 @@ async function setAngularWorkspace(workspacePath: string) {
}

export async function deactivate() {}

function cacheWorkspaceNodeModulesJsons(workspacePath: string) {
if (!existsSync(join(workspacePath, 'node_modules'))) {
getOutputChannel().appendLine(
'Tried to cache node_modules but directory was not present. Run npm install'
);
return;
}

try {
cacheJsonFiles(workspacePath);
} catch (e) {
window.showErrorMessage(
'Angular Console encountered an error when scanning node_modules'
);
getOutputChannel().appendLine('Error parsing node_modules ');

const stringifiedError = e.toString ? e.toString() : JSON.stringify(e);
getOutputChannel().appendLine(stringifiedError);
getTelemetry().exceptionOccured(stringifiedError);
}
}
2 changes: 1 addition & 1 deletion apps/vscode/src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"version": "9.0.0",
"repository": {
"type": "git",
"url": "https://github.com/nrwl/angular-console"
"url": "https://github.com/nrwl/angular-console.git"
},
"author": {
"name": "Narwhal Technologies Inc",
Expand Down
2 changes: 2 additions & 0 deletions libs/server/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { readAndParseJson } from './lib/utils/utils';

export { findClosestNg } from './lib/utils/utils';

export { cacheJsonFiles } from './lib/utils/utils';
Expand Down
Loading

0 comments on commit e343fbd

Please sign in to comment.