Skip to content

Commit

Permalink
feat(vscode): Angular JSON tree provider
Browse files Browse the repository at this point in the history
  • Loading branch information
mrmeku committed Aug 26, 2019
1 parent 002594d commit 588a101
Show file tree
Hide file tree
Showing 15 changed files with 443 additions and 84 deletions.
2 changes: 1 addition & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@
},
"configurations": {
"production": {
"externalDependencies": ["vscode"],
"externalDependencies": ["vscode", "jsonc-parser"],
"sourceMap": false,
"optimization": true,
"extractLicenses": true,
Expand Down
67 changes: 67 additions & 0 deletions apps/vscode/src/app/ng-cli-commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {
commands,
ExtensionContext,
Task,
tasks,
window,
TaskScope
} from 'vscode';

import { NgTaskProvider } from './ng-task-provider/ng-task-provider';
import { NgTaskDefinition } from './ng-task-provider/ng-task-definition';

let ngTaskProvider: NgTaskProvider;
export function registerNgCliCommands(
context: ExtensionContext,
n: NgTaskProvider
) {
ngTaskProvider = n;

['build', 'lint', 'deploy', 'e2e', 'serve', 'test', 'xi18n'].forEach(
command => {
context.subscriptions.push(
commands.registerCommand(
`ng-console.${command}`,
runNgCliCommand(command)
)
);
}
);
}

function runNgCliCommand(command: string) {
return () => {
const items = ngTaskProvider
.getProjectEntries()
.map(([projectName, { architect }]) => ({
projectName,
architectDef: architect && architect[command]
}))
.filter(({ architectDef }) => Boolean(architectDef))
.flatMap(({ architectDef, projectName }) =>
[undefined, ...Object.keys(architectDef!.configurations || {})].map(c =>
c ? `${projectName} --configuration=${c}` : projectName
)
);

window.showQuickPick(items).then(selection => {
if (!selection) {
return;
}

const [projectName, configuration] = selection.split(' --configuration=');
const taskDef: NgTaskDefinition = {
type: 'ng',
projectName,
configuration,
architectName: command
};
const task = ngTaskProvider.resolveTask(
new Task(taskDef, TaskScope.Workspace, 'n/a', 'n/a')
);
if (task) {
tasks.executeTask(task);
}
});
};
}
24 changes: 0 additions & 24 deletions apps/vscode/src/app/ng-task-provider/interfaces.d.ts

This file was deleted.

42 changes: 42 additions & 0 deletions apps/vscode/src/app/ng-task-provider/ng-task-definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
export interface NgTaskDefinition {
type: string;
projectName: string;
architectName: string;
configuration?: string;
}

export interface ArchitectDef {
configurations?: {
[configuration: string]: {};
};
}

export interface ProjectDef {
root: string;
architect?: {
[architectName: string]: ArchitectDef;
};
}

export interface Projects {
[projectName: string]: ProjectDef;
}

export interface AngularJson {
projects: Projects;
}

export function getArchitectTaskDefintions(
defaultTaskDefinition: NgTaskDefinition,
architectDef: ArchitectDef
) {
return [
defaultTaskDefinition,
...Object.keys(architectDef.configurations || {}).map(
(configuration): NgTaskDefinition => ({
...defaultTaskDefinition,
configuration
})
)
];
}
55 changes: 27 additions & 28 deletions apps/vscode/src/app/ng-task-provider/ng-task-provider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { TaskProvider, ProviderResult, Task } from 'vscode';
import { readJsonFile, FileUtils } from '@angular-console/server';
import { getTaskId } from '../pseudo-terminal.factory';
import { NgTaskDefinition, ArchitectDef, AngularJson } from './interfaces';
import {
NgTaskDefinition,
AngularJson,
getArchitectTaskDefintions,
Projects,
ProjectDef
} from './ng-task-definition';
import { NgTask } from './ng-task';

export class NgTaskProvider implements TaskProvider {
Expand All @@ -10,6 +16,9 @@ export class NgTaskProvider implements TaskProvider {

constructor(private readonly fileUtils: FileUtils) {}

getWorkspacePath() {
return this.workspacePath;
}
setWorkspacePath(path: string) {
this.workspacePath = path;
this.ngTasksPromise = undefined;
Expand All @@ -33,7 +42,10 @@ export class NgTaskProvider implements TaskProvider {
const type = getTaskId();
return Object.entries(project.architect).flatMap(
([architectName, architectDef]) =>
getTaskDefs({ architectName, projectName, type }, architectDef)
getArchitectTaskDefintions(
{ architectName, projectName, type },
architectDef
)
);
})
.map(taskDef =>
Expand All @@ -43,49 +55,36 @@ export class NgTaskProvider implements TaskProvider {
return this.ngTasksPromise;
}

resolveTask(task: Task): ProviderResult<Task> {
if (!this.workspacePath) {
return null;
}

resolveTask(task: Task): Task | undefined {
// Make sure that this looks like a NgTaskDefinition.
if (task.definition.architectName && task.definition.projectName) {
if (
this.workspacePath &&
task.definition.architectName &&
task.definition.projectName
) {
return NgTask.create(
{
...(task.definition as NgTaskDefinition),
type: getTaskId()
},
this.workspacePath as string,
this.workspacePath,
this.fileUtils
);
}

return undefined;
}

getProjectEntries() {
getProjects(): Projects {
if (!this.workspacePath) {
return [];
return {};
}

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

return Object.entries(projects || {});
return projects;
}
}

function getTaskDefs(
ngTaskDefinition: NgTaskDefinition,
architectDef: ArchitectDef
) {
return [
ngTaskDefinition,
...Object.keys(architectDef.configurations || {}).map(
(configuration): NgTaskDefinition => ({
...ngTaskDefinition,
configuration
})
)
];
getProjectEntries(): [string, ProjectDef][] {
return Object.entries(this.getProjects() || {});
}
}
16 changes: 14 additions & 2 deletions apps/vscode/src/app/ng-task-provider/ng-task.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NgTaskDefinition } from './interfaces';
import { NgTaskDefinition } from './ng-task-definition';
import { Task, TaskGroup, TaskScope } from 'vscode';
import { getShellExecutionForConfig } from './shell-execution';
import { FileUtils } from '@angular-console/server';
Expand All @@ -15,7 +15,19 @@ export class NgTask extends Task {
? `${projectName}:${architectName}:${configuration}`
: `${projectName}:${architectName}`;

const displayCommand = `ng run ${runTarget}`;
let displayCommand = `ng run ${runTarget}`;
switch (architectName) {
case 'build':
case 'lint':
case 'deploy':
case 'e2e':
case 'serve':
case 'test':
case 'xi18n':
displayCommand = configuration
? `ng ${architectName} ${projectName} --configuration=${configuration}`
: `ng ${architectName} ${projectName}`;
}

const task = new NgTask(
definition,
Expand Down
12 changes: 3 additions & 9 deletions apps/vscode/src/app/pseudo-terminal.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,10 @@ import {
TaskExecution,
TaskRevealKind,
tasks,
TaskScope,
Terminal,
window
TaskScope
} from 'vscode';
import { getShellExecutionForConfig } from './ng-task-provider/shell-execution';

let terminalsToReuse: Array<Terminal> = [];
window.onDidCloseTerminal(e => {
terminalsToReuse = terminalsToReuse.filter(t => t.processId !== e.processId);
});
import { getShellExecutionForConfig } from './ng-task-provider/shell-execution';

const activeTaskNames = new Set<string>();
let currentDryRun: TaskExecution | undefined;
Expand All @@ -42,7 +36,7 @@ export function getTaskId(isDryRun?: boolean): string {
return taskId;
}

function executeTask(config: PseudoTerminalConfig): PseudoTerminal {
export function executeTask(config: PseudoTerminalConfig): PseudoTerminal {
const execution = getShellExecutionForConfig(config);

// Terminating a task shifts editor focus so we wait until the
Expand Down
9 changes: 7 additions & 2 deletions apps/vscode/src/app/start-server.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import {
createServerModule,
QueryResolver,
SelectDirectory
SelectDirectory,
PseudoTerminalFactory
} from '@angular-console/server';
import { NestFactory } from '@nestjs/core';
import * as path from 'path';
import { commands, ExtensionContext, window } from 'vscode';

import { getStoreForContext } from './get-store-for-context';
import { getPseudoTerminalFactory } from './pseudo-terminal.factory';
import { environment } from '../environments/environment';
import { executeTask } from './pseudo-terminal.factory';

function getPseudoTerminalFactory(): PseudoTerminalFactory {
return config => executeTask(config);
}

const getPort = require('get-port'); // tslint:disable-line

Expand Down
Loading

0 comments on commit 588a101

Please sign in to comment.