Skip to content

Commit

Permalink
feat(dep-graph): use xstate for state management (#7634)
Browse files Browse the repository at this point in the history
  • Loading branch information
philipjfulcher committed Nov 9, 2021
1 parent 07c256b commit 5f9279a
Show file tree
Hide file tree
Showing 36 changed files with 1,411 additions and 780 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -11,3 +11,4 @@ tmp
jest.debug.config.js
.tool-versions
/.verdaccio/build/local-registry
dep-graph/dep-graph/src/assets/environment.js
6 changes: 4 additions & 2 deletions dep-graph/dep-graph-e2e/project.json
Expand Up @@ -8,15 +8,17 @@
"options": {
"cypressConfig": "dep-graph/dep-graph-e2e/cypress.json",
"tsConfig": "dep-graph/dep-graph-e2e/tsconfig.e2e.json",
"devServerTarget": "dep-graph-dep-graph:serve"
"devServerTarget": "dep-graph-dep-graph:serve-for-e2e",
"baseUrl": "http://localhost:4200"
}
},
"e2e-watch-disabled": {
"executor": "@nrwl/cypress:cypress",
"options": {
"cypressConfig": "dep-graph/dep-graph-e2e/cypress-watch-mode.json",
"tsConfig": "dep-graph/dep-graph-e2e/tsconfig.e2e.json",
"devServerTarget": "dep-graph-dep-graph:serve:watch"
"devServerTarget": "dep-graph-dep-graph:serve-for-e2e:watch",
"baseUrl": "http://localhost:4200"
}
},
"lint": {
Expand Down
17 changes: 11 additions & 6 deletions dep-graph/dep-graph-e2e/src/integration/app.spec.ts
Expand Up @@ -12,7 +12,12 @@ import {

describe('dep-graph-client', () => {
beforeEach(() => {
cy.intercept('/assets/graphs/*').as('getGraph');

cy.visit('/');

// wait for first graph to finish loading
cy.wait('@getGraph');
});

it('should display message to select projects', () => {
Expand Down Expand Up @@ -51,12 +56,12 @@ describe('dep-graph-client', () => {

describe('selecting projects', () => {
it('should select a project by clicking on the project name', () => {
// cy.get('[data-project="nx-dev"]').should('have.data', 'active', false);
cy.get('[data-project="nx-dev"]')
.click({
force: true,
})
.should('have.data', 'active', true);
cy.get('[data-project="nx-dev"]').should('have.data', 'active', false);
cy.get('[data-project="nx-dev"]').click({
force: true,
});

cy.get('[data-project="nx-dev"]').should('have.data', 'active', true);
});

it('should deselect a project by clicking on the project name again', () => {
Expand Down
78 changes: 44 additions & 34 deletions dep-graph/dep-graph/project.json
Expand Up @@ -32,39 +32,16 @@
},
"configurations": {
"dev": {
"fileReplacements": [
{
"replace": "dep-graph/dep-graph/src/environments/environment.ts",
"with": "dep-graph/dep-graph/src/environments/environment.dev.ts"
}
],
"fileReplacements": [],
"assets": [
"dep-graph/dep-graph/src/favicon.ico",
"dep-graph/dep-graph/src/assets"
],
"optimization": false,
"outputHashing": "none",
"sourceMap": true,
"extractCss": true,
"namedChunks": false,
"extractLicenses": false,
"vendorChunk": true,
"budgets": [
"dep-graph/dep-graph/src/assets/graphs/",
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
},
"watch": {
"fileReplacements": [
{
"replace": "dep-graph/dep-graph/src/environments/environment.ts",
"with": "dep-graph/dep-graph/src/environments/environment.watch.ts"
"input": "dep-graph/dep-graph/src/assets",
"output": "/",
"glob": "environment.js"
}
],
"assets": [],
"optimization": false,
"outputHashing": "none",
"sourceMap": true,
Expand All @@ -83,15 +60,10 @@
},
"outputs": ["{options.outputPath}"]
},
"serve": {
"serve-base": {
"executor": "@nrwl/web:dev-server",
"options": {
"buildTarget": "dep-graph-dep-graph:build-base:dev"
},
"configurations": {
"watch": {
"buildTarget": "dep-graph-dep-graph:build-base:watch"
}
}
},
"lint": {
Expand All @@ -112,6 +84,44 @@
"jestConfig": "dep-graph/dep-graph/jest.config.js",
"passWithNoTests": true
}
},
"serve": {
"executor": "@nrwl/workspace:run-commands",
"outputs": [],
"options": {
"commands": [
"npx ts-node -P ./scripts/tsconfig.scripts.json ./scripts/copy-dep-graph-environment.ts dev",
"nx serve-base dep-graph-dep-graph"
]
},
"configurations": {
"watch": {
"commands": [
"npx ts-node -P ./scripts/tsconfig.scripts.json ./scripts/copy-dep-graph-environment.ts watch",
"nx serve-base dep-graph-dep-graph"
]
}
}
},
"serve-for-e2e": {
"executor": "@nrwl/workspace:run-commands",
"outputs": [],
"options": {
"commands": [
"npx ts-node -P ./scripts/tsconfig.scripts.json ./scripts/copy-dep-graph-environment.ts dev",
"nx serve-base dep-graph-dep-graph"
],
"readyWhen": "No issues found."
},
"configurations": {
"watch": {
"commands": [
"npx ts-node -P ./scripts/tsconfig.scripts.json ./scripts/copy-dep-graph-environment.ts watch",
"nx serve-base dep-graph-dep-graph"
],
"readyWhen": "No issues found."
}
}
}
},
"tags": ["core"]
Expand Down
137 changes: 56 additions & 81 deletions dep-graph/dep-graph/src/app/app.ts
@@ -1,31 +1,38 @@
// nx-ignore-next-line
import type { DepGraphClientResponse } from '@nrwl/workspace/src/command-line/dep-graph';
import { ProjectGraph } from '@nrwl/devkit';
import { combineLatest, fromEvent, Subject } from 'rxjs';
import { startWith, takeUntil } from 'rxjs/operators';
import { fromEvent } from 'rxjs';
import { startWith } from 'rxjs/operators';
import { DebuggerPanel } from './debugger-panel';
import { GraphComponent } from './graph';
import { AppConfig, DEFAULT_CONFIG } from './models';
import { useDepGraphService } from './machines/dep-graph.service';
import { DepGraphSend } from './machines/interfaces';
import { AppConfig, DEFAULT_CONFIG, ProjectGraphService } from './models';
import { GraphTooltipService } from './tooltip-service';
import { SidebarComponent } from './ui-sidebar/sidebar';

export class AppComponent {
private sidebar: SidebarComponent;
private sidebar = new SidebarComponent();
private tooltipService = new GraphTooltipService();
private graph = new GraphComponent(this.tooltipService);
private debuggerPanel: DebuggerPanel;

private windowResize$ = fromEvent(window, 'resize').pipe(startWith({}));
private render$ = new Subject<{ newProjects: string[] }>();

constructor(private config: AppConfig = DEFAULT_CONFIG) {
this.render$.subscribe((nextRenderConfig) => this.render(nextRenderConfig));
private send: DepGraphSend;

constructor(
private config: AppConfig = DEFAULT_CONFIG,
private projectGraphService: ProjectGraphService
) {
const [_, send] = useDepGraphService();
this.send = send;

this.loadProjectGraph(config.defaultProjectGraph);
this.render();

if (window.watch === true) {
setInterval(
() => this.loadProjectGraph(config.defaultProjectGraph),
() => this.updateProjectGraph(config.defaultProjectGraph),
5000
);
}
Expand All @@ -37,39 +44,49 @@ export class AppComponent {
);

const project: DepGraphClientResponse =
await this.config.projectGraphService.getProjectGraph(projectInfo.url);
await this.projectGraphService.getProjectGraph(projectInfo.url);

const workspaceLayout = project?.layout;

const nodes = Object.values(project.projects).reduce((acc, cur: any) => {
acc[cur.name] = cur;
return acc;
}, {});
this.send({
type: 'initGraph',
projects: project.projects,
dependencies: project.dependencies,
affectedProjects: project.affected,
workspaceLayout: workspaceLayout,
});

if (!!window.focusedProject) {
this.send({
type: 'focusProject',
projectName: window.focusedProject,
});
}

if (window.groupByFolder) {
this.send({
type: 'setGroupByFolder',
groupByFolder: window.groupByFolder,
});
}
}

const newProjects = !!window.graph
? project.changes.added.filter(
(addedProject) => !window.graph.nodes[addedProject]
)
: project.changes.added;
private async updateProjectGraph(projectGraphId: string) {
const projectInfo = this.config.projectGraphs.find(
(graph) => graph.id === projectGraphId
);

window.projects = project.projects;
window.graph = <ProjectGraph>{
const project: DepGraphClientResponse =
await this.projectGraphService.getProjectGraph(projectInfo.url);

this.send({
type: 'updateGraph',
projects: project.projects,
dependencies: project.dependencies,
nodes: nodes,
};
window.focusedProject = null;
window.projectGraphList = this.config.projectGraphs;
window.selectedProjectGraph = projectGraphId;
window.workspaceLayout = workspaceLayout;

if (this.sidebar) {
this.render$.next({ newProjects });
} else {
this.render$.next();
}
});
}

private render(renderConfig: { newProjects: string[] } | undefined) {
private render() {
const debuggerPanelContainer = document.getElementById('debugger-panel');

if (this.config.showDebugger) {
Expand All @@ -78,59 +95,17 @@ export class AppComponent {

this.debuggerPanel = new DebuggerPanel(
debuggerPanelContainer,
window.projectGraphList
this.config.projectGraphs,
this.config.defaultProjectGraph
);

this.debuggerPanel.selectProject$.subscribe((id) => {
this.loadProjectGraph(id);
this.sidebar.resetSidebarVisibility();
});
}

this.graph.projectGraph = window.graph;
const affectedProjects = window.affected;

this.graph.affectedProjects = affectedProjects;

if (!this.sidebar) {
this.sidebar = new SidebarComponent(affectedProjects);
} else {
this.sidebar.projects = window.projects;

if (renderConfig?.newProjects.length > 0) {
this.sidebar.selectProjects(renderConfig.newProjects);
}
}

combineLatest([
this.sidebar.selectedProjectsChanged$,
this.sidebar.groupByFolderChanged$,
this.windowResize$,
])
.pipe(takeUntil(this.render$))
.subscribe(([selectedProjectNames, groupByFolder]) => {
const selectedProjects = [];

selectedProjectNames.forEach((projectName) => {
if (window.graph.nodes[projectName]) {
selectedProjects.push(window.graph.nodes[projectName]);
}
});

if (selectedProjects.length === 0) {
document.getElementById('no-projects-chosen').style.display = 'flex';
} else {
document.getElementById('no-projects-chosen').style.display = 'none';
this.graph.render(selectedProjects, groupByFolder);
}
});

if (this.debuggerPanel) {
this.graph.renderTimes$
.pipe(takeUntil(this.render$))
.subscribe(
(renderTime) => (this.debuggerPanel.renderTime = renderTime)
);
this.graph.renderTimes$.subscribe(
(renderTime) => (this.debuggerPanel.renderTime = renderTime)
);
}
}
}
5 changes: 3 additions & 2 deletions dep-graph/dep-graph/src/app/debugger-panel.ts
Expand Up @@ -16,7 +16,8 @@ export class DebuggerPanel {

constructor(
private container: HTMLElement,
private projectGraphs: ProjectGraphList[]
private projectGraphs: ProjectGraphList[],
private initialSelectedGraph: string
) {
this.render();
}
Expand All @@ -40,7 +41,7 @@ export class DebuggerPanel {
select.appendChild(option);
});

select.value = window.selectedProjectGraph;
select.value = this.initialSelectedGraph;
select.dataset['cy'] = 'project-select';

select.onchange = (event) =>
Expand Down

1 comment on commit 5f9279a

@vercel
Copy link

@vercel vercel bot commented on 5f9279a Nov 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.