Skip to content

Commit

Permalink
fix(error-handling): Give more feedback for common errors
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 committed Nov 12, 2019
1 parent a161c32 commit 493438e
Show file tree
Hide file tree
Showing 10 changed files with 352 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 493438e

Please sign in to comment.