feat(graph): add project details view (#20466)
Co-authored-by: FrozenPandaz <jasonjean1993@gmail.com>
This commit is contained in:
parent
a08fdf0e36
commit
75cc561e9d
1
.gitignore
vendored
1
.gitignore
vendored
@ -19,6 +19,7 @@ jest.debug.config.js
|
||||
/graph/client/src/assets/generated-project-graphs
|
||||
/graph/client/src/assets/generated-task-graphs
|
||||
/graph/client/src/assets/generated-task-inputs
|
||||
/graph/client/src/assets/generated-source-maps
|
||||
/nx-dev/nx-dev/public/documentation
|
||||
/nx-dev/nx-dev/public/images/open-graph
|
||||
|
||||
|
||||
@ -68,6 +68,7 @@
|
||||
"graph/client/src/assets/generated-project-graphs/",
|
||||
"graph/client/src/assets/generated-task-graphs/",
|
||||
"graph/client/src/assets/generated-task-inputs/",
|
||||
"graph/client/src/assets/generated-source-maps/",
|
||||
{
|
||||
"input": "graph/client/src/assets/dev",
|
||||
"output": "/",
|
||||
@ -81,6 +82,7 @@
|
||||
"graph/client/src/assets/project-graphs/",
|
||||
"graph/client/src/assets/task-graphs/",
|
||||
"graph/client/src/assets/task-inputs/",
|
||||
"graph/client/src/assets/source-maps/",
|
||||
{
|
||||
"input": "graph/client/src/assets/dev-e2e",
|
||||
"output": "/",
|
||||
@ -116,6 +118,11 @@
|
||||
"output": "/assets/task-graphs",
|
||||
"glob": "e2e.json"
|
||||
},
|
||||
{
|
||||
"input": "graph/client/src/assets/source-maps",
|
||||
"output": "/assets/source-maps",
|
||||
"glob": "e2e.json"
|
||||
},
|
||||
{
|
||||
"input": "graph/client/src/assets/release",
|
||||
"output": "/",
|
||||
|
||||
@ -34,6 +34,16 @@ export class FetchProjectGraphService implements ProjectGraphService {
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async getSourceMaps(
|
||||
url: string
|
||||
): Promise<Record<string, Record<string, string[]>>> {
|
||||
const request = new Request(url, { mode: 'no-cors' });
|
||||
|
||||
const response = await fetch(request);
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
setTaskInputsUrl(url: string) {
|
||||
this.taskInputsUrl = url;
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ export interface WorkspaceData {
|
||||
projectGraphUrl: string;
|
||||
taskGraphUrl: string;
|
||||
taskInputsUrl: string;
|
||||
sourceMapsUrl: string;
|
||||
}
|
||||
|
||||
export interface WorkspaceLayout {
|
||||
@ -25,6 +26,9 @@ export interface ProjectGraphService {
|
||||
getTaskGraph: (url: string) => Promise<TaskGraphClientResponse>;
|
||||
setTaskInputsUrl?: (url: string) => void;
|
||||
getExpandedTaskInputs?: (taskId: string) => Promise<Record<string, string[]>>;
|
||||
getSourceMaps?: (
|
||||
url: string
|
||||
) => Promise<Record<string, Record<string, string[]>>>;
|
||||
}
|
||||
|
||||
export interface Environment {
|
||||
|
||||
@ -27,4 +27,10 @@ export class LocalProjectGraphService implements ProjectGraphService {
|
||||
resolve(window.expandedTaskInputsResponse[taskId])
|
||||
);
|
||||
}
|
||||
|
||||
async getSourceMaps(
|
||||
url: string
|
||||
): Promise<Record<string, Record<string, string[]>>> {
|
||||
return new Promise((resolve) => resolve(window.sourceMapsResponse));
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import { ProjectGraphClientResponse } from 'nx/src/command-line/graph/graph';
|
||||
/* eslint-enable @nx/enforce-module-boundaries */
|
||||
import { getProjectGraphDataService } from './hooks/get-project-graph-data-service';
|
||||
import { TasksSidebarErrorBoundary } from './feature-tasks/tasks-sidebar-error-boundary';
|
||||
import { ProjectDetails } from '@nx/graph/project-details';
|
||||
|
||||
const { appConfig } = getEnvironmentConfig();
|
||||
const projectGraphDataService = getProjectGraphDataService();
|
||||
@ -47,11 +48,37 @@ const workspaceDataLoader = async (selectedWorkspaceId: string) => {
|
||||
};
|
||||
|
||||
const taskDataLoader = async (selectedWorkspaceId: string) => {
|
||||
const projectInfo = appConfig.workspaces.find(
|
||||
const workspaceInfo = appConfig.workspaces.find(
|
||||
(graph) => graph.id === selectedWorkspaceId
|
||||
);
|
||||
|
||||
return await projectGraphDataService.getTaskGraph(projectInfo.taskGraphUrl);
|
||||
return await projectGraphDataService.getTaskGraph(workspaceInfo.taskGraphUrl);
|
||||
};
|
||||
|
||||
const sourceMapsLoader = async (selectedWorkspaceId: string) => {
|
||||
const workspaceInfo = appConfig.workspaces.find(
|
||||
(graph) => graph.id === selectedWorkspaceId
|
||||
);
|
||||
|
||||
return await projectGraphDataService.getSourceMaps(
|
||||
workspaceInfo.sourceMapsUrl
|
||||
);
|
||||
};
|
||||
|
||||
const projectDetailsLoader = async (
|
||||
selectedWorkspaceId: string,
|
||||
projectName: string
|
||||
) => {
|
||||
const workspaceData = await workspaceDataLoader(selectedWorkspaceId);
|
||||
const sourceMaps = await sourceMapsLoader(selectedWorkspaceId);
|
||||
|
||||
const project = workspaceData.projects.find(
|
||||
(project) => project.name === projectName
|
||||
);
|
||||
return {
|
||||
project,
|
||||
sourceMap: sourceMaps[project.data.root],
|
||||
};
|
||||
};
|
||||
|
||||
const childRoutes: RouteObject[] = [
|
||||
@ -146,6 +173,15 @@ export const devRoutes: RouteObject[] = [
|
||||
},
|
||||
children: childRoutes,
|
||||
},
|
||||
{
|
||||
path: ':selectedWorkspaceId/project-details/:projectName',
|
||||
id: 'selectedProjectDetails',
|
||||
element: <ProjectDetails />,
|
||||
loader: async ({ request, params }) => {
|
||||
const projectName = params.projectName;
|
||||
return projectDetailsLoader(params.selectedWorkspaceId, projectName);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@ -174,4 +210,13 @@ export const releaseRoutes: RouteObject[] = [
|
||||
...childRoutes,
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'project-details/:projectName',
|
||||
id: 'selectedProjectDetails',
|
||||
element: <ProjectDetails />,
|
||||
loader: async ({ request, params }) => {
|
||||
const projectName = params.projectName;
|
||||
return projectDetailsLoader(appConfig.defaultWorkspaceId, projectName);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@ -13,6 +13,7 @@ window.appConfig = {
|
||||
projectGraphUrl: 'assets/project-graphs/e2e.json',
|
||||
taskGraphUrl: 'assets/task-graphs/e2e.json',
|
||||
taskInputsUrl: 'assets/task-inputs/e2e.json',
|
||||
sourceMapsUrl: 'assets/source-maps/e2e.json',
|
||||
},
|
||||
],
|
||||
defaultWorkspaceId: 'local',
|
||||
|
||||
1753
graph/client/src/assets/source-maps/e2e-affected.json
Normal file
1753
graph/client/src/assets/source-maps/e2e-affected.json
Normal file
File diff suppressed because it is too large
Load Diff
1753
graph/client/src/assets/source-maps/e2e.json
Normal file
1753
graph/client/src/assets/source-maps/e2e.json
Normal file
File diff suppressed because it is too large
Load Diff
4
graph/client/src/globals.d.ts
vendored
4
graph/client/src/globals.d.ts
vendored
@ -8,6 +8,9 @@ import type {
|
||||
/* eslint-enable @nx/enforce-module-boundaries */
|
||||
import { AppConfig } from './app/interfaces';
|
||||
import { ExternalApi } from './app/external-api';
|
||||
/* eslint-disable @nx/enforce-module-boundaries */
|
||||
// nx-ignore-next-line
|
||||
import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration-utils';
|
||||
|
||||
export declare global {
|
||||
export interface Window {
|
||||
@ -17,6 +20,7 @@ export declare global {
|
||||
projectGraphResponse?: ProjectGraphClientResponse;
|
||||
taskGraphResponse?: TaskGraphClientResponse;
|
||||
expandedTaskInputsResponse?: ExpandedTaskInputsReponse;
|
||||
sourceMapsResponse?: ConfigurationSourceMaps;
|
||||
environment: 'dev' | 'watch' | 'release' | 'nx-console';
|
||||
appConfig: AppConfig;
|
||||
useXstateInspect: boolean;
|
||||
|
||||
12
graph/project-details/.babelrc
Normal file
12
graph/project-details/.babelrc
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
18
graph/project-details/.eslintrc.json
Normal file
18
graph/project-details/.eslintrc.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
7
graph/project-details/README.md
Normal file
7
graph/project-details/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# project-details
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test project-details` to execute the unit tests via [Jest](https://jestjs.io).
|
||||
16
graph/project-details/project.json
Normal file
16
graph/project-details/project.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "graph-project-details",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "graph/project-details/src",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["graph/project-details/**/*.{ts,tsx,js,jsx}"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
graph/project-details/src/index.ts
Normal file
1
graph/project-details/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './lib/project-details';
|
||||
16
graph/project-details/src/lib/get-source-information.ts
Normal file
16
graph/project-details/src/lib/get-source-information.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export function getSourceInformation(
|
||||
sourceMap: Record<string, string[]>,
|
||||
key: string
|
||||
): string | undefined {
|
||||
const sourceInfo = sourceMap?.[key];
|
||||
if (sourceInfo) {
|
||||
return `${key} was set by plugin \n \n ${sourceInfo[1]} \n \n while processing \n \n ${sourceInfo[0]}`;
|
||||
}
|
||||
if (!key.includes('.')) {
|
||||
return undefined;
|
||||
}
|
||||
return getSourceInformation(
|
||||
sourceMap,
|
||||
key.substring(0, key.lastIndexOf('.'))
|
||||
);
|
||||
}
|
||||
66
graph/project-details/src/lib/project-details.tsx
Normal file
66
graph/project-details/src/lib/project-details.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import styles from './app.module.css';
|
||||
import Target from './target';
|
||||
|
||||
import PropertyRenderer from './property-renderer';
|
||||
import { useRouteLoaderData } from 'react-router-dom';
|
||||
|
||||
/* eslint-disable @nx/enforce-module-boundaries */
|
||||
// nx-ignore-next-line
|
||||
import { ProjectGraphProjectNode } from '@nx/devkit';
|
||||
|
||||
export function ProjectDetails() {
|
||||
const {
|
||||
project: {
|
||||
name,
|
||||
data: { targets, root, ...projectData },
|
||||
},
|
||||
sourceMap,
|
||||
} = useRouteLoaderData('selectedProjectDetails') as {
|
||||
project: ProjectGraphProjectNode;
|
||||
sourceMap: Record<string, string[]>;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="m-4 overflow-auto w-full">
|
||||
<h1 className="text-2xl">{name}</h1>
|
||||
<h2 className="text-lg pl-6 mb-3 flex flex-row gap-2">
|
||||
{root}{' '}
|
||||
{projectData.tags?.map((tag) => (
|
||||
<p className="bg-slate-300">{tag}</p>
|
||||
))}
|
||||
</h2>
|
||||
<div>
|
||||
<div className="mb-2">
|
||||
<h2 className="text-xl">Targets</h2>
|
||||
{Object.entries(targets ?? {}).map(([targetName, target]) =>
|
||||
Target({
|
||||
targetName: targetName,
|
||||
targetConfiguration: target,
|
||||
sourceMap,
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
{Object.entries(projectData).map(([key, value]) => {
|
||||
if (
|
||||
key === 'targets' ||
|
||||
key === 'root' ||
|
||||
key === 'name' ||
|
||||
key === '$schema' ||
|
||||
key === 'tags' ||
|
||||
key === 'files'
|
||||
)
|
||||
return undefined;
|
||||
|
||||
return PropertyRenderer({
|
||||
propertyKey: key,
|
||||
propertyValue: value,
|
||||
sourceMap,
|
||||
});
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ProjectDetails;
|
||||
116
graph/project-details/src/lib/property-renderer.tsx
Normal file
116
graph/project-details/src/lib/property-renderer.tsx
Normal file
@ -0,0 +1,116 @@
|
||||
import { getSourceInformation } from './get-source-information';
|
||||
import { useState } from 'react';
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
export interface PropertyRendererProps {
|
||||
propertyKey: string;
|
||||
propertyValue: any;
|
||||
keyPrefix?: string;
|
||||
sourceMap: Record<string, string[]>;
|
||||
}
|
||||
|
||||
export function PropertyRenderer(props: PropertyRendererProps) {
|
||||
const { propertyValue, propertyKey, sourceMap, keyPrefix } = props;
|
||||
const sourceMapKey = `${keyPrefix ? `${keyPrefix}.` : ''}${propertyKey}`;
|
||||
const isCollapsible = propertyValue && typeof propertyValue === 'object';
|
||||
const [isCollapsed, setIsCollapsed] = useState(true);
|
||||
|
||||
const toggleCollapse = () => {
|
||||
setIsCollapsed(!isCollapsed);
|
||||
};
|
||||
|
||||
return (
|
||||
<div title={getSourceInformation(sourceMap, sourceMapKey)}>
|
||||
{isCollapsible && (
|
||||
<button className="text-xs" onClick={toggleCollapse}>
|
||||
{isCollapsed ? '\u25B6' : '\u25BC'}
|
||||
</button>
|
||||
)}
|
||||
<span className="font-medium">{propertyKey}</span>:{' '}
|
||||
{renderOpening(propertyValue)}
|
||||
{!isCollapsed || !isCollapsible ? (
|
||||
<PropertyValueRenderer {...props} />
|
||||
) : (
|
||||
'...'
|
||||
)}
|
||||
{renderClosing(propertyValue)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type PropertValueRendererProps = PropertyRendererProps & {
|
||||
nested?: boolean;
|
||||
};
|
||||
|
||||
function PropertyValueRenderer(props: PropertValueRendererProps) {
|
||||
const { propertyKey, propertyValue, sourceMap, keyPrefix, nested } = props;
|
||||
|
||||
if (Array.isArray(propertyValue) && propertyValue.length) {
|
||||
return (
|
||||
<div className="ml-3">
|
||||
{nested && renderOpening(propertyValue)}
|
||||
{propertyValue.map((v) =>
|
||||
PropertyValueRenderer({
|
||||
propertyKey,
|
||||
propertyValue: v,
|
||||
sourceMap,
|
||||
keyPrefix: `${keyPrefix ? `${keyPrefix}.` : ''}${v}`,
|
||||
nested: true,
|
||||
})
|
||||
)}
|
||||
{nested && renderClosing(propertyValue)}
|
||||
</div>
|
||||
);
|
||||
} else if (propertyValue && typeof propertyValue === 'object') {
|
||||
return (
|
||||
<div
|
||||
title={getSourceInformation(
|
||||
sourceMap,
|
||||
`${keyPrefix ? `${keyPrefix}.` : ''}${propertyKey}`
|
||||
)}
|
||||
>
|
||||
{nested && renderOpening(propertyValue)}
|
||||
<div className="ml-3">
|
||||
{Object.entries(propertyValue)
|
||||
.filter(
|
||||
([, value]) =>
|
||||
value && (Array.isArray(value) ? value.length : true)
|
||||
)
|
||||
.map(([key, value]) =>
|
||||
PropertyRenderer({
|
||||
propertyKey: key,
|
||||
propertyValue: value,
|
||||
keyPrefix: `${keyPrefix ? `${keyPrefix}.` : ''}${propertyKey}`,
|
||||
sourceMap,
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
{nested && renderClosing(propertyValue)}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<code>{`${propertyValue}`}</code>,
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function renderOpening(value: any): string {
|
||||
return Array.isArray(value) && value.length
|
||||
? '['
|
||||
: value && typeof value === 'object'
|
||||
? '{'
|
||||
: '';
|
||||
}
|
||||
|
||||
function renderClosing(value: any): string {
|
||||
return Array.isArray(value) && value.length
|
||||
? '],'
|
||||
: value && typeof value === 'object'
|
||||
? '},'
|
||||
: '';
|
||||
}
|
||||
|
||||
export default PropertyRenderer;
|
||||
10
graph/project-details/src/lib/target.spec.tsx
Normal file
10
graph/project-details/src/lib/target.spec.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import Target from './target';
|
||||
|
||||
describe('Target', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<Target />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
32
graph/project-details/src/lib/target.tsx
Normal file
32
graph/project-details/src/lib/target.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
/* eslint-disable @nx/enforce-module-boundaries */
|
||||
// nx-ignore-next-line
|
||||
import { TargetConfiguration } from '@nx/devkit';
|
||||
import PropertyRenderer from './property-renderer';
|
||||
import { useState } from 'react';
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
export interface TargetProps {
|
||||
targetName: string;
|
||||
targetConfiguration: TargetConfiguration;
|
||||
sourceMap: Record<string, string[]>;
|
||||
}
|
||||
|
||||
export function Target(props: TargetProps) {
|
||||
return (
|
||||
<div className="ml-3 mb-3">
|
||||
<h3 className="text-lg font-bold">{props.targetName}</h3>
|
||||
<div className="ml-3">
|
||||
{Object.entries(props.targetConfiguration).map(([key, value]) =>
|
||||
PropertyRenderer({
|
||||
propertyKey: key,
|
||||
propertyValue: value,
|
||||
keyPrefix: `targets.${props.targetName}`,
|
||||
sourceMap: props.sourceMap,
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Target;
|
||||
17
graph/project-details/tsconfig.json
Normal file
17
graph/project-details/tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": false,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../tsconfig.base.json"
|
||||
}
|
||||
24
graph/project-details/tsconfig.lib.json
Normal file
24
graph/project-details/tsconfig.lib.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": [
|
||||
"node",
|
||||
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.test.jsx"
|
||||
],
|
||||
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { buildProjectGraphWithoutDaemon } from '../src/project-graph/project-graph';
|
||||
import { buildProjectGraphAndSourceMapsWithoutDaemon } from '../src/project-graph/project-graph';
|
||||
import { workspaceRoot } from '../src/utils/workspace-root';
|
||||
import { fileExists } from '../src/utils/fileutils';
|
||||
import { join } from 'path';
|
||||
@ -20,7 +20,9 @@ import { setupWorkspaceContext } from '../src/utils/workspace-context';
|
||||
try {
|
||||
await daemonClient.stop();
|
||||
} catch (e) {}
|
||||
const tasks: Array<Promise<any>> = [buildProjectGraphWithoutDaemon()];
|
||||
const tasks: Array<Promise<any>> = [
|
||||
buildProjectGraphAndSourceMapsWithoutDaemon(),
|
||||
];
|
||||
if (isNxCloudUsed(readNxJson())) {
|
||||
tasks.push(verifyOrUpdateNxCloudClient(getCloudOptions()));
|
||||
}
|
||||
|
||||
@ -33,7 +33,10 @@ import { TaskGraph } from '../../config/task-graph';
|
||||
import { daemonClient } from '../../daemon/client/client';
|
||||
import { getRootTsConfigPath } from '../../plugins/js/utils/typescript';
|
||||
import { pruneExternalNodes } from '../../project-graph/operators';
|
||||
import { createProjectGraphAsync } from '../../project-graph/project-graph';
|
||||
import {
|
||||
createProjectGraphAndSourceMapsAsync,
|
||||
createProjectGraphAsync,
|
||||
} from '../../project-graph/project-graph';
|
||||
import {
|
||||
createTaskGraph,
|
||||
mapTargetDefaultsToDependencies,
|
||||
@ -45,6 +48,7 @@ import { HashPlanner, transferProjectGraph } from '../../native';
|
||||
import { transformProjectGraphForRust } from '../../native/transform-objects';
|
||||
import { getAffectedGraphNodes } from '../affected/affected';
|
||||
import { readFileMapCache } from '../../project-graph/nx-deps-cache';
|
||||
import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration-utils';
|
||||
|
||||
import { filterUsingGlobPatterns } from '../../hasher/task-hasher';
|
||||
|
||||
@ -94,7 +98,8 @@ function buildEnvironmentJs(
|
||||
localMode: 'build' | 'serve',
|
||||
depGraphClientResponse?: ProjectGraphClientResponse,
|
||||
taskGraphClientResponse?: TaskGraphClientResponse,
|
||||
expandedTaskInputsReponse?: ExpandedTaskInputsReponse
|
||||
expandedTaskInputsReponse?: ExpandedTaskInputsReponse,
|
||||
sourceMapsResponse?: ConfigurationSourceMaps
|
||||
) {
|
||||
let environmentJs = `window.exclude = ${JSON.stringify(exclude)};
|
||||
window.watch = ${!!watchMode};
|
||||
@ -111,6 +116,7 @@ function buildEnvironmentJs(
|
||||
projectGraphUrl: 'project-graph.json',
|
||||
taskGraphUrl: 'task-graph.json',
|
||||
taskInputsUrl: 'task-inputs.json',
|
||||
sourceMapsUrl: 'source-maps.json'
|
||||
}
|
||||
],
|
||||
defaultWorkspaceId: 'local',
|
||||
@ -130,10 +136,15 @@ function buildEnvironmentJs(
|
||||
environmentJs += `window.expandedTaskInputsResponse = ${JSON.stringify(
|
||||
expandedTaskInputsReponse
|
||||
)};`;
|
||||
|
||||
environmentJs += `window.sourceMapsResponse = ${JSON.stringify(
|
||||
sourceMapsResponse
|
||||
)};`;
|
||||
} else {
|
||||
environmentJs += `window.projectGraphResponse = null;`;
|
||||
environmentJs += `window.taskGraphResponse = null;`;
|
||||
environmentJs += `window.expandedTaskInputsResponse = null;`;
|
||||
environmentJs += `window.sourceMapsResponse = null;`;
|
||||
}
|
||||
|
||||
return environmentJs;
|
||||
@ -214,7 +225,7 @@ export async function generateGraph(
|
||||
groupByFolder?: boolean;
|
||||
watch?: boolean;
|
||||
open?: boolean;
|
||||
view: 'projects' | 'tasks';
|
||||
view: 'projects' | 'tasks' | 'project-details';
|
||||
projects?: string[];
|
||||
all?: boolean;
|
||||
targets?: string[];
|
||||
@ -238,12 +249,33 @@ export async function generateGraph(
|
||||
});
|
||||
}
|
||||
|
||||
if (args.view === 'project-details' && !args.focus) {
|
||||
output.error({
|
||||
title: `The project details view requires the --focus option.`,
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
if (args.view === 'project-details' && (args.targets || args.affected)) {
|
||||
output.error({
|
||||
title: `The project details view can only be used with the --focus option.`,
|
||||
bodyLines: [
|
||||
`You passed ${args.targets ? '--targets ' : ''}${
|
||||
args.affected ? '--affected ' : ''
|
||||
}`,
|
||||
],
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// TODO: Graph Client should support multiple targets
|
||||
const target = Array.isArray(args.targets && args.targets.length >= 1)
|
||||
? args.targets[0]
|
||||
: args.targets;
|
||||
|
||||
const rawGraph = await createProjectGraphAsync({ exitOnError: true });
|
||||
const { projectGraph: rawGraph, sourceMaps } =
|
||||
await createProjectGraphAndSourceMapsAsync({
|
||||
exitOnError: true,
|
||||
});
|
||||
let prunedGraph = pruneExternalNodes(rawGraph);
|
||||
|
||||
const projects = Object.values(
|
||||
@ -339,23 +371,23 @@ export async function generateGraph(
|
||||
},
|
||||
});
|
||||
|
||||
const depGraphClientResponse = await createDepGraphClientResponse(
|
||||
affectedProjects
|
||||
);
|
||||
const { projectGraphClientResponse } =
|
||||
await createProjectGraphAndSourceMapClientResponse(affectedProjects);
|
||||
|
||||
const taskGraphClientResponse = await createTaskGraphClientResponse();
|
||||
const taskInputsReponse = await createExpandedTaskInputResponse(
|
||||
taskGraphClientResponse,
|
||||
depGraphClientResponse
|
||||
projectGraphClientResponse
|
||||
);
|
||||
|
||||
const environmentJs = buildEnvironmentJs(
|
||||
args.exclude || [],
|
||||
args.watch,
|
||||
!!args.file && args.file.endsWith('html') ? 'build' : 'serve',
|
||||
depGraphClientResponse,
|
||||
projectGraphClientResponse,
|
||||
taskGraphClientResponse,
|
||||
taskInputsReponse
|
||||
taskInputsReponse,
|
||||
sourceMaps
|
||||
);
|
||||
html = html.replace(/src="/g, 'src="static/');
|
||||
html = html.replace(/href="styles/g, 'href="static/styles');
|
||||
@ -472,10 +504,13 @@ async function startServer(
|
||||
unregisterFileWatcher = await createFileWatcher();
|
||||
}
|
||||
|
||||
currentDepGraphClientResponse = await createDepGraphClientResponse(affected);
|
||||
currentDepGraphClientResponse.focus = focus;
|
||||
currentDepGraphClientResponse.groupByFolder = groupByFolder;
|
||||
currentDepGraphClientResponse.exclude = exclude;
|
||||
const { projectGraphClientResponse, sourceMapResponse } =
|
||||
await createProjectGraphAndSourceMapClientResponse(affected);
|
||||
|
||||
currentProjectGraphClientResponse = projectGraphClientResponse;
|
||||
currentProjectGraphClientResponse.focus = focus;
|
||||
currentProjectGraphClientResponse.groupByFolder = groupByFolder;
|
||||
currentProjectGraphClientResponse.exclude = exclude;
|
||||
|
||||
const app = http.createServer(async (req, res) => {
|
||||
// parse URL
|
||||
@ -488,7 +523,7 @@ async function startServer(
|
||||
const sanitizePath = basename(parsedUrl.pathname);
|
||||
if (sanitizePath === 'project-graph.json') {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify(currentDepGraphClientResponse));
|
||||
res.end(JSON.stringify(currentProjectGraphClientResponse));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -515,9 +550,15 @@ async function startServer(
|
||||
return;
|
||||
}
|
||||
|
||||
if (sanitizePath === 'source-maps.json') {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify(currentSourceMapsClientResponse));
|
||||
return;
|
||||
}
|
||||
|
||||
if (sanitizePath === 'currentHash') {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ hash: currentDepGraphClientResponse.hash }));
|
||||
res.end(JSON.stringify({ hash: currentProjectGraphClientResponse.hash }));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -563,7 +604,7 @@ async function startServer(
|
||||
});
|
||||
}
|
||||
|
||||
let currentDepGraphClientResponse: ProjectGraphClientResponse = {
|
||||
let currentProjectGraphClientResponse: ProjectGraphClientResponse = {
|
||||
hash: null,
|
||||
projects: [],
|
||||
dependencies: {},
|
||||
@ -577,6 +618,7 @@ let currentDepGraphClientResponse: ProjectGraphClientResponse = {
|
||||
groupByFolder: false,
|
||||
exclude: [],
|
||||
};
|
||||
let currentSourceMapsClientResponse: ConfigurationSourceMaps = {};
|
||||
|
||||
function debounce(fn: (...args) => void, time: number) {
|
||||
let timeout: NodeJS.Timeout;
|
||||
@ -602,14 +644,17 @@ function createFileWatcher() {
|
||||
} else if (changes !== null && changes.changedFiles.length > 0) {
|
||||
output.note({ title: 'Recalculating project graph...' });
|
||||
|
||||
const newGraphClientResponse = await createDepGraphClientResponse();
|
||||
const { projectGraphClientResponse, sourceMapResponse } =
|
||||
await createProjectGraphAndSourceMapClientResponse();
|
||||
|
||||
if (
|
||||
newGraphClientResponse.hash !== currentDepGraphClientResponse.hash
|
||||
projectGraphClientResponse.hash !==
|
||||
currentProjectGraphClientResponse.hash
|
||||
) {
|
||||
output.note({ title: 'Graph changes updated.' });
|
||||
|
||||
currentDepGraphClientResponse = newGraphClientResponse;
|
||||
currentProjectGraphClientResponse = projectGraphClientResponse;
|
||||
currentSourceMapsClientResponse = sourceMapResponse;
|
||||
} else {
|
||||
output.note({ title: 'No graph changes found.' });
|
||||
}
|
||||
@ -618,14 +663,18 @@ function createFileWatcher() {
|
||||
);
|
||||
}
|
||||
|
||||
async function createDepGraphClientResponse(
|
||||
async function createProjectGraphAndSourceMapClientResponse(
|
||||
affected: string[] = []
|
||||
): Promise<ProjectGraphClientResponse> {
|
||||
): Promise<{
|
||||
projectGraphClientResponse: ProjectGraphClientResponse;
|
||||
sourceMapResponse: ConfigurationSourceMaps;
|
||||
}> {
|
||||
performance.mark('project graph watch calculation:start');
|
||||
|
||||
let graph = pruneExternalNodes(
|
||||
await createProjectGraphAsync({ exitOnError: true })
|
||||
);
|
||||
const { projectGraph, sourceMaps } =
|
||||
await createProjectGraphAndSourceMapsAsync({ exitOnError: true });
|
||||
|
||||
let graph = pruneExternalNodes(projectGraph);
|
||||
let fileMap = readFileMapCache().fileMap.projectFileMap;
|
||||
performance.mark('project graph watch calculation:end');
|
||||
performance.mark('project graph response generation:start');
|
||||
@ -654,13 +703,16 @@ async function createDepGraphClientResponse(
|
||||
);
|
||||
|
||||
return {
|
||||
...currentDepGraphClientResponse,
|
||||
projectGraphClientResponse: {
|
||||
...currentProjectGraphClientResponse,
|
||||
hash,
|
||||
layout,
|
||||
projects,
|
||||
dependencies,
|
||||
affected,
|
||||
fileMap,
|
||||
},
|
||||
sourceMapResponse: sourceMaps,
|
||||
};
|
||||
}
|
||||
|
||||
@ -846,9 +898,11 @@ async function getExpandedTaskInputs(
|
||||
if (inputs) {
|
||||
return expandInputs(
|
||||
inputs,
|
||||
currentDepGraphClientResponse.projects.find((p) => p.name === project),
|
||||
currentProjectGraphClientResponse.projects.find(
|
||||
(p) => p.name === project
|
||||
),
|
||||
allWorkspaceFiles,
|
||||
currentDepGraphClientResponse
|
||||
currentProjectGraphClientResponse
|
||||
);
|
||||
}
|
||||
return {};
|
||||
|
||||
@ -21,6 +21,7 @@ export type ShowProjectsOptions = NxShowArgs & {
|
||||
|
||||
export type ShowProjectOptions = NxShowArgs & {
|
||||
projectName: string;
|
||||
web?: boolean;
|
||||
};
|
||||
|
||||
export const yargsShowCommand: CommandModule<
|
||||
@ -121,6 +122,17 @@ const showProjectCommand: CommandModule<NxShowArgs, ShowProjectOptions> = {
|
||||
description: 'Which project should be viewed?',
|
||||
})
|
||||
.default('json', true)
|
||||
.option('web', {
|
||||
type: 'boolean',
|
||||
description: 'Show project details in the browser',
|
||||
hidden: true,
|
||||
})
|
||||
.check((argv) => {
|
||||
if (argv.web) {
|
||||
argv.json = false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.example(
|
||||
'$0 show project my-app',
|
||||
'View project information for my-app in JSON format'
|
||||
|
||||
@ -19,6 +19,7 @@ import {
|
||||
} from '../../utils/command-line-utils';
|
||||
import { findMatchingProjects } from '../../utils/find-matching-projects';
|
||||
import { ShowProjectOptions, ShowProjectsOptions } from './command-object';
|
||||
import { generateGraph } from '../graph/graph';
|
||||
|
||||
export async function showProjectsHandler(
|
||||
args: ShowProjectsOptions
|
||||
@ -94,6 +95,16 @@ export async function showProjectHandler(
|
||||
}
|
||||
if (args.json) {
|
||||
console.log(JSON.stringify(node.data));
|
||||
} else if (args.web) {
|
||||
await generateGraph(
|
||||
{
|
||||
view: 'project-details',
|
||||
focus: node.name,
|
||||
watch: false,
|
||||
open: true,
|
||||
},
|
||||
[]
|
||||
);
|
||||
} else {
|
||||
const chalk = require('chalk') as typeof import('chalk');
|
||||
const logIfExists = (label, key: keyof typeof node['data']) => {
|
||||
|
||||
@ -24,6 +24,7 @@ import { Message, SocketMessenger } from './socket-messenger';
|
||||
import { safelyCleanUpExistingProcess } from '../cache';
|
||||
import { Hash } from '../../hasher/task-hasher';
|
||||
import { Task, TaskGraph } from '../../config/task-graph';
|
||||
import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration-utils';
|
||||
|
||||
const DAEMON_ENV_SETTINGS = {
|
||||
...process.env,
|
||||
@ -124,9 +125,17 @@ export class DaemonClient {
|
||||
return this.sendToDaemonViaQueue({ type: 'REQUEST_SHUTDOWN' });
|
||||
}
|
||||
|
||||
async getProjectGraph(): Promise<ProjectGraph> {
|
||||
return (await this.sendToDaemonViaQueue({ type: 'REQUEST_PROJECT_GRAPH' }))
|
||||
.projectGraph;
|
||||
async getProjectGraphAndSourceMaps(): Promise<{
|
||||
projectGraph: ProjectGraph;
|
||||
sourceMaps: ConfigurationSourceMaps;
|
||||
}> {
|
||||
const response = await this.sendToDaemonViaQueue({
|
||||
type: 'REQUEST_PROJECT_GRAPH',
|
||||
});
|
||||
return {
|
||||
projectGraph: response.projectGraph,
|
||||
sourceMaps: response.sourceMaps,
|
||||
};
|
||||
}
|
||||
|
||||
async getAllFileData(): Promise<FileData[]> {
|
||||
@ -162,7 +171,7 @@ export class DaemonClient {
|
||||
} | null
|
||||
) => void
|
||||
): Promise<UnregisterCallback> {
|
||||
await this.getProjectGraph();
|
||||
await this.getProjectGraphAndSourceMaps();
|
||||
let messenger: SocketMessenger | undefined;
|
||||
|
||||
await this.queue.sendToQueue(() => {
|
||||
|
||||
@ -19,7 +19,8 @@ export async function handleRequestProjectGraph(): Promise<HandlerResult> {
|
||||
|
||||
const serializedResult = serializeResult(
|
||||
result.error,
|
||||
result.serializedProjectGraph
|
||||
result.serializedProjectGraph,
|
||||
result.serializedSourceMaps
|
||||
);
|
||||
if (!serializedResult) {
|
||||
return {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { performance } from 'perf_hooks';
|
||||
import { readNxJson, NxJsonConfiguration } from '../../config/nx-json';
|
||||
import { readNxJson } from '../../config/nx-json';
|
||||
import {
|
||||
FileData,
|
||||
FileMap,
|
||||
@ -21,7 +21,6 @@ import {
|
||||
retrieveWorkspaceFiles,
|
||||
} from '../../project-graph/utils/retrieve-workspace-files';
|
||||
import { fileExists } from '../../utils/fileutils';
|
||||
import { writeSourceMaps } from '../../utils/source-maps';
|
||||
import {
|
||||
resetWorkspaceContext,
|
||||
updateFilesInContext,
|
||||
@ -31,14 +30,17 @@ import { notifyFileWatcherSockets } from './file-watching/file-watcher-sockets';
|
||||
import { serverLogger } from './logger';
|
||||
import { NxWorkspaceFilesExternals } from '../../native';
|
||||
|
||||
let cachedSerializedProjectGraphPromise: Promise<{
|
||||
interface SerializedProjectGraph {
|
||||
error: Error | null;
|
||||
projectGraph: ProjectGraph | null;
|
||||
fileMap: FileMap | null;
|
||||
allWorkspaceFiles: FileData[] | null;
|
||||
serializedProjectGraph: string | null;
|
||||
serializedSourceMaps: string | null;
|
||||
rustReferences: NxWorkspaceFilesExternals | null;
|
||||
}>;
|
||||
}
|
||||
|
||||
let cachedSerializedProjectGraphPromise: Promise<SerializedProjectGraph>;
|
||||
export let fileMapWithFiles:
|
||||
| {
|
||||
fileMap: FileMap;
|
||||
@ -56,7 +58,7 @@ let waitPeriod = 100;
|
||||
let scheduledTimeoutId;
|
||||
let knownExternalNodes: Record<string, ProjectGraphExternalNode> = {};
|
||||
|
||||
export async function getCachedSerializedProjectGraphPromise() {
|
||||
export async function getCachedSerializedProjectGraphPromise(): Promise<SerializedProjectGraph> {
|
||||
try {
|
||||
// recomputing it now on demand. we can ignore the scheduled timeout
|
||||
if (scheduledTimeoutId) {
|
||||
@ -81,6 +83,7 @@ export async function getCachedSerializedProjectGraphPromise() {
|
||||
return {
|
||||
error: e,
|
||||
serializedProjectGraph: null,
|
||||
serializedSourceMaps: null,
|
||||
projectGraph: null,
|
||||
fileMap: null,
|
||||
allWorkspaceFiles: null,
|
||||
@ -196,7 +199,7 @@ async function processCollectedUpdatedAndDeletedFiles(
|
||||
}
|
||||
}
|
||||
|
||||
async function processFilesAndCreateAndSerializeProjectGraph() {
|
||||
async function processFilesAndCreateAndSerializeProjectGraph(): Promise<SerializedProjectGraph> {
|
||||
try {
|
||||
performance.mark('hash-watched-changes-start');
|
||||
const updatedFiles = [...collectedUpdatedFiles.values()];
|
||||
@ -214,17 +217,16 @@ async function processFilesAndCreateAndSerializeProjectGraph() {
|
||||
serverLogger.requestLog([...updatedFiles.values()]);
|
||||
serverLogger.requestLog([...deletedFiles]);
|
||||
const nxJson = readNxJson(workspaceRoot);
|
||||
const configResult = await retrieveProjectConfigurations(
|
||||
const graphNodes = await retrieveProjectConfigurations(
|
||||
workspaceRoot,
|
||||
nxJson
|
||||
);
|
||||
await processCollectedUpdatedAndDeletedFiles(
|
||||
configResult,
|
||||
graphNodes,
|
||||
updatedFileHashes,
|
||||
deletedFiles
|
||||
);
|
||||
writeSourceMaps(configResult.sourceMaps);
|
||||
return createAndSerializeProjectGraph(configResult);
|
||||
return createAndSerializeProjectGraph(graphNodes);
|
||||
} catch (err) {
|
||||
return Promise.resolve({
|
||||
error: err,
|
||||
@ -233,6 +235,7 @@ async function processFilesAndCreateAndSerializeProjectGraph() {
|
||||
rustReferences: null,
|
||||
allWorkspaceFiles: null,
|
||||
serializedProjectGraph: null,
|
||||
serializedSourceMaps: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -254,14 +257,8 @@ function copyFileMap(m: FileMap) {
|
||||
|
||||
async function createAndSerializeProjectGraph({
|
||||
projects,
|
||||
}: RetrievedGraphNodes): Promise<{
|
||||
error: string | null;
|
||||
projectGraph: ProjectGraph | null;
|
||||
fileMap: FileMap | null;
|
||||
allWorkspaceFiles: FileData[] | null;
|
||||
rustReferences: NxWorkspaceFilesExternals | null;
|
||||
serializedProjectGraph: string | null;
|
||||
}> {
|
||||
sourceMaps,
|
||||
}: RetrievedGraphNodes): Promise<SerializedProjectGraph> {
|
||||
try {
|
||||
performance.mark('create-project-graph-start');
|
||||
const fileMap = copyFileMap(fileMapWithFiles.fileMap);
|
||||
@ -289,6 +286,7 @@ async function createAndSerializeProjectGraph({
|
||||
|
||||
performance.mark('json-stringify-start');
|
||||
const serializedProjectGraph = JSON.stringify(projectGraph);
|
||||
const serializedSourceMaps = JSON.stringify(sourceMaps);
|
||||
performance.mark('json-stringify-end');
|
||||
performance.measure(
|
||||
'serialize graph',
|
||||
@ -302,6 +300,7 @@ async function createAndSerializeProjectGraph({
|
||||
fileMap,
|
||||
allWorkspaceFiles,
|
||||
serializedProjectGraph,
|
||||
serializedSourceMaps,
|
||||
rustReferences,
|
||||
};
|
||||
} catch (e) {
|
||||
@ -314,6 +313,7 @@ async function createAndSerializeProjectGraph({
|
||||
fileMap: null,
|
||||
allWorkspaceFiles: null,
|
||||
serializedProjectGraph: null,
|
||||
serializedSourceMaps: null,
|
||||
rustReferences: null,
|
||||
};
|
||||
}
|
||||
|
||||
@ -99,6 +99,6 @@ export async function respondWithErrorAndExit(
|
||||
|
||||
error.message = `${error.message}\n\nBecause of the error the Nx daemon process has exited. The next Nx command is going to restart the daemon process.\nIf the error persists, please run "nx reset".`;
|
||||
|
||||
await respondToClient(socket, serializeResult(error, null), null);
|
||||
await respondToClient(socket, serializeResult(error, null, null), null);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@ -32,10 +32,11 @@ function serializeError(error: Error | null): string | null {
|
||||
// Prepare a serialized project graph result for sending over IPC from the server to the client
|
||||
export function serializeResult(
|
||||
error: Error | null,
|
||||
serializedProjectGraph: string | null
|
||||
serializedProjectGraph: string | null,
|
||||
serializedSourceMaps: string | null
|
||||
): string | null {
|
||||
// We do not want to repeat work `JSON.stringify`ing an object containing the potentially large project graph so merge as strings
|
||||
return `{ "error": ${serializeError(
|
||||
error
|
||||
)}, "projectGraph": ${serializedProjectGraph} }`;
|
||||
)}, "projectGraph": ${serializedProjectGraph}, "sourceMaps": ${serializedSourceMaps} }`;
|
||||
}
|
||||
|
||||
@ -18,7 +18,6 @@ import {
|
||||
} from './utils/retrieve-workspace-files';
|
||||
import { readNxJson } from '../config/nx-json';
|
||||
import { unregisterPluginTSTranspiler } from '../utils/nx-plugin';
|
||||
import { writeSourceMaps } from '../utils/source-maps';
|
||||
|
||||
/**
|
||||
* Synchronously reads the latest cached copy of the workspace's ProjectGraph.
|
||||
@ -78,7 +77,7 @@ export function readProjectsConfigurationFromProjectGraph(
|
||||
};
|
||||
}
|
||||
|
||||
export async function buildProjectGraphWithoutDaemon() {
|
||||
export async function buildProjectGraphAndSourceMapsWithoutDaemon() {
|
||||
const nxJson = readNxJson();
|
||||
|
||||
performance.mark('retrieve-project-configurations:start');
|
||||
@ -106,11 +105,9 @@ export async function buildProjectGraphWithoutDaemon() {
|
||||
).projectGraph;
|
||||
performance.mark('build-project-graph-using-project-file-map:end');
|
||||
|
||||
writeSourceMaps(sourceMaps);
|
||||
|
||||
unregisterPluginTSTranspiler();
|
||||
|
||||
return projectGraph;
|
||||
return { projectGraph, sourceMaps };
|
||||
}
|
||||
|
||||
function handleProjectGraphError(opts: { exitOnError: boolean }, e) {
|
||||
@ -156,11 +153,23 @@ export async function createProjectGraphAsync(
|
||||
resetDaemonClient: false,
|
||||
}
|
||||
): Promise<ProjectGraph> {
|
||||
const projectGraphAndSourceMaps = await createProjectGraphAndSourceMapsAsync(
|
||||
opts
|
||||
);
|
||||
return projectGraphAndSourceMaps.projectGraph;
|
||||
}
|
||||
|
||||
export async function createProjectGraphAndSourceMapsAsync(
|
||||
opts: { exitOnError: boolean; resetDaemonClient?: boolean } = {
|
||||
exitOnError: false,
|
||||
resetDaemonClient: false,
|
||||
}
|
||||
) {
|
||||
performance.mark('create-project-graph-async:start');
|
||||
|
||||
if (!daemonClient.enabled()) {
|
||||
try {
|
||||
const res = await buildProjectGraphWithoutDaemon();
|
||||
const res = await buildProjectGraphAndSourceMapsWithoutDaemon();
|
||||
performance.measure(
|
||||
'create-project-graph-async >> retrieve-project-configurations',
|
||||
'retrieve-project-configurations:start',
|
||||
@ -188,7 +197,8 @@ export async function createProjectGraphAsync(
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const projectGraph = await daemonClient.getProjectGraph();
|
||||
const projectGraphAndSourceMaps =
|
||||
await daemonClient.getProjectGraphAndSourceMaps();
|
||||
if (opts.resetDaemonClient) {
|
||||
daemonClient.reset();
|
||||
}
|
||||
@ -198,7 +208,7 @@ export async function createProjectGraphAsync(
|
||||
'create-project-graph-async:start',
|
||||
'create-project-graph-async:end'
|
||||
);
|
||||
return projectGraph;
|
||||
return projectGraphAndSourceMaps;
|
||||
} catch (e) {
|
||||
if (e.message.indexOf('inotify_add_watch') > -1) {
|
||||
// common errors with the daemon due to OS settings (cannot watch all the files available)
|
||||
@ -210,7 +220,7 @@ export async function createProjectGraphAsync(
|
||||
],
|
||||
});
|
||||
markDaemonAsDisabled();
|
||||
return buildProjectGraphWithoutDaemon();
|
||||
return buildProjectGraphAndSourceMapsWithoutDaemon();
|
||||
}
|
||||
|
||||
if (e.internalDaemonError) {
|
||||
@ -224,7 +234,7 @@ export async function createProjectGraphAsync(
|
||||
],
|
||||
});
|
||||
markDaemonAsDisabled();
|
||||
return buildProjectGraphWithoutDaemon();
|
||||
return buildProjectGraphAndSourceMapsWithoutDaemon();
|
||||
}
|
||||
|
||||
handleProjectGraphError(opts, e);
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
import { performance } from 'perf_hooks';
|
||||
import { getNxRequirePaths } from '../../utils/installation-directory';
|
||||
import {
|
||||
ProjectConfiguration,
|
||||
ProjectsConfigurations,
|
||||
} from '../../config/workspace-json-project-json';
|
||||
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||
import {
|
||||
NX_ANGULAR_JSON_PLUGIN_NAME,
|
||||
NxAngularJsonPlugin,
|
||||
@ -11,11 +8,10 @@ import {
|
||||
} from '../../adapter/angular-json';
|
||||
import { NxJsonConfiguration, readNxJson } from '../../config/nx-json';
|
||||
import { ProjectGraphExternalNode } from '../../config/project-graph';
|
||||
import type { NxWorkspaceFiles } from '../../native';
|
||||
import { getNxPackageJsonWorkspacesPlugin } from '../../../plugins/package-json-workspaces';
|
||||
import {
|
||||
ConfigurationSourceMaps,
|
||||
buildProjectsConfigurationsFromProjectPathsAndPlugins,
|
||||
ConfigurationSourceMaps,
|
||||
} from './project-configuration-utils';
|
||||
import {
|
||||
getDefaultPlugins,
|
||||
@ -24,8 +20,8 @@ import {
|
||||
} from '../../utils/nx-plugin';
|
||||
import { CreateProjectJsonProjectsPlugin } from '../../plugins/project-json/build-nodes/project-json';
|
||||
import {
|
||||
globWithWorkspaceContext,
|
||||
getNxWorkspaceFilesFromContext,
|
||||
globWithWorkspaceContext,
|
||||
} from '../../utils/workspace-context';
|
||||
import { buildAllWorkspaceFiles } from './build-all-workspace-files';
|
||||
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
import { join } from 'path';
|
||||
import { projectGraphCacheDirectory } from './cache-directory';
|
||||
import { writeJsonFile } from './fileutils';
|
||||
|
||||
export function writeSourceMaps(
|
||||
sourceMaps: Record<string, Record<string, string[]>>
|
||||
) {
|
||||
writeJsonFile(
|
||||
join(projectGraphCacheDirectory, 'configuration-source-maps.json'),
|
||||
sourceMaps
|
||||
);
|
||||
}
|
||||
@ -34,6 +34,7 @@ function writeFile() {
|
||||
projectGraphUrl: join('assets/generated-project-graphs/', filename),
|
||||
taskGraphUrl: join('assets/generated-task-graphs/', filename),
|
||||
taskInputsUrl: join('assets/generated-task-inputs/', filename),
|
||||
sourceMapsUrl: join('assets/generated-source-maps/', filename),
|
||||
};
|
||||
});
|
||||
} catch {
|
||||
@ -52,6 +53,7 @@ function writeFile() {
|
||||
projectGraphUrl: join('assets/project-graphs/', filename),
|
||||
taskGraphUrl: join('assets/task-graphs/', filename),
|
||||
taskInputsUrl: join('assets/task-inputs/', filename),
|
||||
sourceMapsUrl: join('assets/source-maps/', filename),
|
||||
};
|
||||
});
|
||||
} catch {
|
||||
|
||||
@ -36,6 +36,10 @@ async function generateGraph(directory: string, name: string) {
|
||||
/window.expandedTaskInputsResponse = (.*?);/
|
||||
);
|
||||
|
||||
const sourceMapsResponse = environmentJs.match(
|
||||
/window.sourceMapsResponse = (.*?);/
|
||||
);
|
||||
|
||||
ensureDirSync(
|
||||
join(__dirname, '../graph/client/src/assets/generated-project-graphs/')
|
||||
);
|
||||
@ -45,6 +49,9 @@ async function generateGraph(directory: string, name: string) {
|
||||
ensureDirSync(
|
||||
join(__dirname, '../graph/client/src/assets/generated-task-inputs/')
|
||||
);
|
||||
ensureDirSync(
|
||||
join(__dirname, '../graph/client/src/assets/generated-source-maps/')
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
join(
|
||||
@ -72,6 +79,15 @@ async function generateGraph(directory: string, name: string) {
|
||||
),
|
||||
expandedTaskInputsReponse[1]
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
join(
|
||||
__dirname,
|
||||
'../graph/client/src/assets/generated-source-maps/',
|
||||
`${name}.json`
|
||||
),
|
||||
sourceMapsResponse[1]
|
||||
);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
|
||||
@ -35,6 +35,7 @@
|
||||
"@nx/expo": ["packages/expo"],
|
||||
"@nx/expo/*": ["packages/expo/*"],
|
||||
"@nx/express": ["packages/express"],
|
||||
"@nx/graph/project-details": ["graph/project-details/src/index.ts"],
|
||||
"@nx/graph/ui-components": ["graph/ui-components/src/index.ts"],
|
||||
"@nx/graph/ui-graph": ["graph/ui-graph/src/index.ts"],
|
||||
"@nx/graph/ui-tooltips": ["graph/ui-tooltips/src/index.ts"],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user