feat(core): add API entrypoint to register metadata (#22773)
This commit is contained in:
parent
99e5c869b3
commit
7bb6e9ee14
25
docs/generated/devkit/CreateMetadata.md
Normal file
25
docs/generated/devkit/CreateMetadata.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Type alias: CreateMetadata\<T\>
|
||||
|
||||
Ƭ **CreateMetadata**\<`T`\>: (`graph`: [`ProjectGraph`](../../devkit/documents/ProjectGraph), `options`: `T` \| `undefined`, `context`: [`CreateMetadataContext`](../../devkit/documents/CreateMetadataContext)) => [`ProjectsMetadata`](../../devkit/documents/ProjectsMetadata) \| `Promise`\<[`ProjectsMetadata`](../../devkit/documents/ProjectsMetadata)\>
|
||||
|
||||
#### Type parameters
|
||||
|
||||
| Name | Type |
|
||||
| :--- | :-------- |
|
||||
| `T` | `unknown` |
|
||||
|
||||
#### Type declaration
|
||||
|
||||
▸ (`graph`, `options`, `context`): [`ProjectsMetadata`](../../devkit/documents/ProjectsMetadata) \| `Promise`\<[`ProjectsMetadata`](../../devkit/documents/ProjectsMetadata)\>
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :-------- | :---------------------------------------------------------------------- |
|
||||
| `graph` | [`ProjectGraph`](../../devkit/documents/ProjectGraph) |
|
||||
| `options` | `T` \| `undefined` |
|
||||
| `context` | [`CreateMetadataContext`](../../devkit/documents/CreateMetadataContext) |
|
||||
|
||||
##### Returns
|
||||
|
||||
[`ProjectsMetadata`](../../devkit/documents/ProjectsMetadata) \| `Promise`\<[`ProjectsMetadata`](../../devkit/documents/ProjectsMetadata)\>
|
||||
10
docs/generated/devkit/CreateMetadataContext.md
Normal file
10
docs/generated/devkit/CreateMetadataContext.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Type alias: CreateMetadataContext
|
||||
|
||||
Ƭ **CreateMetadataContext**: `Object`
|
||||
|
||||
#### Type declaration
|
||||
|
||||
| Name | Type |
|
||||
| :-------------------- | :------------------------------------------------------------------ |
|
||||
| `nxJsonConfiguration` | [`NxJsonConfiguration`](../../devkit/documents/NxJsonConfiguration) |
|
||||
| `workspaceRoot` | `string` |
|
||||
@ -15,5 +15,6 @@ A plugin for Nx which creates nodes and dependencies for the [ProjectGraph](../.
|
||||
| Name | Type | Description |
|
||||
| :-------------------- | :------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `createDependencies?` | [`CreateDependencies`](../../devkit/documents/CreateDependencies)\<`TOptions`\> | Provides a function to analyze files to create dependencies for the [ProjectGraph](../../devkit/documents/ProjectGraph) |
|
||||
| `createMetadata?` | [`CreateMetadata`](../../devkit/documents/CreateMetadata)\<`TOptions`\> | Provides a function to create metadata for the [ProjectGraph](../../devkit/documents/ProjectGraph) |
|
||||
| `createNodes?` | [`CreateNodes`](../../devkit/documents/CreateNodes)\<`TOptions`\> | Provides a file pattern and function that retrieves configuration info from those files. e.g. { '\*_/_.csproj': buildProjectsFromCsProjFile } |
|
||||
| `name` | `string` | - |
|
||||
|
||||
3
docs/generated/devkit/ProjectsMetadata.md
Normal file
3
docs/generated/devkit/ProjectsMetadata.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Type alias: ProjectsMetadata
|
||||
|
||||
Ƭ **ProjectsMetadata**: `Record`\<`string`, `Pick`\<[`ProjectConfiguration`](../../devkit/documents/ProjectConfiguration), `"metadata"`\>\>
|
||||
@ -64,6 +64,8 @@ It only uses language primitives and immutable objects
|
||||
### Type Aliases
|
||||
|
||||
- [CreateDependencies](../../devkit/documents/CreateDependencies)
|
||||
- [CreateMetadata](../../devkit/documents/CreateMetadata)
|
||||
- [CreateMetadataContext](../../devkit/documents/CreateMetadataContext)
|
||||
- [CreateNodes](../../devkit/documents/CreateNodes)
|
||||
- [CreateNodesFunction](../../devkit/documents/CreateNodesFunction)
|
||||
- [CustomHasher](../../devkit/documents/CustomHasher)
|
||||
@ -83,6 +85,7 @@ It only uses language primitives and immutable objects
|
||||
- [ProjectGraphNode](../../devkit/documents/ProjectGraphNode)
|
||||
- [ProjectTargetConfigurator](../../devkit/documents/ProjectTargetConfigurator)
|
||||
- [ProjectType](../../devkit/documents/ProjectType)
|
||||
- [ProjectsMetadata](../../devkit/documents/ProjectsMetadata)
|
||||
- [RawProjectGraphDependency](../../devkit/documents/RawProjectGraphDependency)
|
||||
- [StaticDependency](../../devkit/documents/StaticDependency)
|
||||
- [StringChange](../../devkit/documents/StringChange)
|
||||
|
||||
@ -64,6 +64,8 @@ It only uses language primitives and immutable objects
|
||||
### Type Aliases
|
||||
|
||||
- [CreateDependencies](../../devkit/documents/CreateDependencies)
|
||||
- [CreateMetadata](../../devkit/documents/CreateMetadata)
|
||||
- [CreateMetadataContext](../../devkit/documents/CreateMetadataContext)
|
||||
- [CreateNodes](../../devkit/documents/CreateNodes)
|
||||
- [CreateNodesFunction](../../devkit/documents/CreateNodesFunction)
|
||||
- [CustomHasher](../../devkit/documents/CustomHasher)
|
||||
@ -83,6 +85,7 @@ It only uses language primitives and immutable objects
|
||||
- [ProjectGraphNode](../../devkit/documents/ProjectGraphNode)
|
||||
- [ProjectTargetConfigurator](../../devkit/documents/ProjectTargetConfigurator)
|
||||
- [ProjectType](../../devkit/documents/ProjectType)
|
||||
- [ProjectsMetadata](../../devkit/documents/ProjectsMetadata)
|
||||
- [RawProjectGraphDependency](../../devkit/documents/RawProjectGraphDependency)
|
||||
- [StaticDependency](../../devkit/documents/StaticDependency)
|
||||
- [StringChange](../../devkit/documents/StringChange)
|
||||
|
||||
@ -20,12 +20,24 @@ export default async function* execute(
|
||||
`;
|
||||
|
||||
export const NX_PLUGIN_V2_CONTENTS = `import { basename, dirname } from "path";
|
||||
import { CreateNodes } from "@nx/devkit";
|
||||
import { CreateNodes, CreateMetadata, ProjectsMetadata } from "@nx/devkit";
|
||||
|
||||
type PluginOptions = {
|
||||
inferredTags: string[]
|
||||
}
|
||||
|
||||
export const createMetadata: CreateMetadata = (graph) => {
|
||||
const metadata: ProjectsMetadata = {};
|
||||
for (const projectNode of Object.values(graph.nodes)) {
|
||||
metadata[projectNode.name] = {
|
||||
metadata: {
|
||||
technologies: ["my-plugin"]
|
||||
}
|
||||
}
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
export const createNodes: CreateNodes<PluginOptions> = [
|
||||
"**/my-project-file",
|
||||
(f, options, ctx) => {
|
||||
|
||||
@ -335,6 +335,7 @@ describe('Nx Plugin', () => {
|
||||
runCLI(`show project ${inferredProject} --json`)
|
||||
);
|
||||
expect(configuration.tags).toEqual(['my-tag']);
|
||||
expect(configuration.metadata.technologies).toEqual(['my-plugin']);
|
||||
});
|
||||
|
||||
it('should be able to use local generators and executors', async () => {
|
||||
|
||||
@ -8,10 +8,7 @@ import {
|
||||
} from '../../config/project-graph';
|
||||
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||
import { hashArray } from '../../hasher/file-hasher';
|
||||
import {
|
||||
buildProjectGraphUsingProjectFileMap as buildProjectGraphUsingFileMap,
|
||||
CreateDependenciesError,
|
||||
} from '../../project-graph/build-project-graph';
|
||||
import { buildProjectGraphUsingProjectFileMap as buildProjectGraphUsingFileMap } from '../../project-graph/build-project-graph';
|
||||
import { updateFileMap } from '../../project-graph/file-map-utils';
|
||||
import {
|
||||
FileMapCache,
|
||||
@ -38,6 +35,7 @@ import { getPlugins } from './plugins';
|
||||
import {
|
||||
DaemonProjectGraphError,
|
||||
ProjectConfigurationsError,
|
||||
isAggregateProjectGraphError,
|
||||
} from '../../project-graph/error-types';
|
||||
|
||||
interface SerializedProjectGraph {
|
||||
@ -263,7 +261,7 @@ async function processFilesAndCreateAndSerializeProjectGraph(
|
||||
const errors = [...(projectConfigurationsError?.errors ?? [])];
|
||||
|
||||
if (g.error) {
|
||||
if (g.error instanceof CreateDependenciesError) {
|
||||
if (isAggregateProjectGraphError(g.error)) {
|
||||
errors.push(...g.error.errors);
|
||||
} else {
|
||||
return {
|
||||
@ -344,7 +342,8 @@ async function createAndSerializeProjectGraph({
|
||||
allWorkspaceFiles,
|
||||
rustReferences,
|
||||
currentProjectFileMapCache || readFileMapCache(),
|
||||
await getPlugins()
|
||||
await getPlugins(),
|
||||
sourceMaps
|
||||
);
|
||||
|
||||
currentProjectFileMapCache = projectFileMapCache;
|
||||
|
||||
@ -54,6 +54,9 @@ export type {
|
||||
CreateNodesContext,
|
||||
CreateDependencies,
|
||||
CreateDependenciesContext,
|
||||
CreateMetadata,
|
||||
CreateMetadataContext,
|
||||
ProjectsMetadata,
|
||||
} from './project-graph/plugins';
|
||||
|
||||
export type {
|
||||
|
||||
@ -9,13 +9,16 @@ import {
|
||||
extractCachedFileData,
|
||||
FileMapCache,
|
||||
shouldRecomputeWholeGraph,
|
||||
writeCache,
|
||||
} from './nx-deps-cache';
|
||||
import { applyImplicitDependencies } from './utils/implicit-project-dependencies';
|
||||
import { normalizeProjectNodes } from './utils/normalize-project-nodes';
|
||||
import { LoadedNxPlugin } from './plugins/internal-api';
|
||||
import { isNxPluginV1, isNxPluginV2 } from './plugins/utils';
|
||||
import { CreateDependenciesContext } from './plugins';
|
||||
import {
|
||||
CreateDependenciesContext,
|
||||
CreateMetadataContext,
|
||||
ProjectsMetadata,
|
||||
} from './plugins';
|
||||
import { getRootTsConfigPath } from '../plugins/js/utils/typescript';
|
||||
import {
|
||||
FileMap,
|
||||
@ -31,6 +34,17 @@ import { existsSync } from 'fs';
|
||||
import { PackageJson } from '../utils/package-json';
|
||||
import { output } from '../utils/output';
|
||||
import { NxWorkspaceFilesExternals } from '../native';
|
||||
import {
|
||||
AggregateProjectGraphError,
|
||||
CreateMetadataError,
|
||||
isAggregateProjectGraphError,
|
||||
ProcessDependenciesError,
|
||||
ProcessProjectGraphError,
|
||||
} from './error-types';
|
||||
import {
|
||||
ConfigurationSourceMaps,
|
||||
mergeMetadata,
|
||||
} from './utils/project-configuration-utils';
|
||||
|
||||
let storedFileMap: FileMap | null = null;
|
||||
let storedAllWorkspaceFiles: FileData[] | null = null;
|
||||
@ -66,7 +80,8 @@ export async function buildProjectGraphUsingProjectFileMap(
|
||||
allWorkspaceFiles: FileData[],
|
||||
rustReferences: NxWorkspaceFilesExternals,
|
||||
fileMapCache: FileMapCache | null,
|
||||
plugins: LoadedNxPlugin[]
|
||||
plugins: LoadedNxPlugin[],
|
||||
sourceMap: ConfigurationSourceMaps
|
||||
): Promise<{
|
||||
projectGraph: ProjectGraph;
|
||||
projectFileMapCache: FileMapCache;
|
||||
@ -122,7 +137,8 @@ export async function buildProjectGraphUsingProjectFileMap(
|
||||
context,
|
||||
cachedFileData,
|
||||
projectGraphVersion,
|
||||
plugins
|
||||
plugins,
|
||||
sourceMap
|
||||
);
|
||||
const projectFileMapCache = createProjectFileMapCache(
|
||||
nxJson,
|
||||
@ -165,7 +181,8 @@ async function buildProjectGraphUsingContext(
|
||||
ctx: CreateDependenciesContext,
|
||||
cachedFileData: CachedFileData,
|
||||
projectGraphVersion: string,
|
||||
plugins: LoadedNxPlugin[]
|
||||
plugins: LoadedNxPlugin[],
|
||||
sourceMap: ConfigurationSourceMaps
|
||||
) {
|
||||
performance.mark('build project graph:start');
|
||||
|
||||
@ -179,15 +196,16 @@ async function buildProjectGraphUsingContext(
|
||||
const initProjectGraph = builder.getUpdatedProjectGraph();
|
||||
|
||||
let updatedGraph;
|
||||
let error;
|
||||
let error: AggregateProjectGraphError;
|
||||
try {
|
||||
updatedGraph = await updateProjectGraphWithPlugins(
|
||||
ctx,
|
||||
initProjectGraph,
|
||||
plugins
|
||||
plugins,
|
||||
sourceMap
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof CreateDependenciesError) {
|
||||
if (isAggregateProjectGraphError(e)) {
|
||||
updatedGraph = e.partialProjectGraph;
|
||||
error = e;
|
||||
} else {
|
||||
@ -228,7 +246,7 @@ async function buildProjectGraphUsingContext(
|
||||
if (!error) {
|
||||
return finalGraph;
|
||||
} else {
|
||||
throw new CreateDependenciesError(error.errors, finalGraph);
|
||||
throw new AggregateProjectGraphError(error.errors, finalGraph);
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,10 +270,13 @@ function createContext(
|
||||
async function updateProjectGraphWithPlugins(
|
||||
context: CreateDependenciesContext,
|
||||
initProjectGraph: ProjectGraph,
|
||||
plugins: LoadedNxPlugin[]
|
||||
plugins: LoadedNxPlugin[],
|
||||
sourceMap: ConfigurationSourceMaps
|
||||
) {
|
||||
let graph = initProjectGraph;
|
||||
const errors: Array<ProcessDependenciesError | ProcessProjectGraphError> = [];
|
||||
const errors: Array<
|
||||
ProcessDependenciesError | ProcessProjectGraphError | CreateMetadataError
|
||||
> = [];
|
||||
for (const plugin of plugins) {
|
||||
try {
|
||||
if (
|
||||
@ -343,51 +364,26 @@ async function updateProjectGraphWithPlugins(
|
||||
})
|
||||
);
|
||||
|
||||
const result = builder.getUpdatedProjectGraph();
|
||||
const graphWithDeps = builder.getUpdatedProjectGraph();
|
||||
|
||||
if (errors.length === 0) {
|
||||
return result;
|
||||
} else {
|
||||
throw new CreateDependenciesError(errors, result);
|
||||
}
|
||||
}
|
||||
|
||||
export class ProcessDependenciesError extends Error {
|
||||
constructor(public readonly pluginName: string, { cause }) {
|
||||
super(
|
||||
`The "${pluginName}" plugin threw an error while creating dependencies:`,
|
||||
const { errors: metadataErrors, graph: updatedGraph } =
|
||||
await applyProjectMetadata(
|
||||
graphWithDeps,
|
||||
plugins,
|
||||
{
|
||||
cause,
|
||||
}
|
||||
nxJsonConfiguration: context.nxJsonConfiguration,
|
||||
workspaceRoot,
|
||||
},
|
||||
sourceMap
|
||||
);
|
||||
this.name = this.constructor.name;
|
||||
this.stack = `${this.message}\n ${cause.stack.split('\n').join('\n ')}`;
|
||||
}
|
||||
|
||||
errors.push(...metadataErrors);
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new AggregateProjectGraphError(errors, updatedGraph);
|
||||
}
|
||||
|
||||
export class ProcessProjectGraphError extends Error {
|
||||
constructor(public readonly pluginName: string, { cause }) {
|
||||
super(
|
||||
`The "${pluginName}" plugin threw an error while processing the project graph:`,
|
||||
{
|
||||
cause,
|
||||
}
|
||||
);
|
||||
this.name = this.constructor.name;
|
||||
this.stack = `${this.message}\n ${cause.stack.split('\n').join('\n ')}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class CreateDependenciesError extends Error {
|
||||
constructor(
|
||||
public readonly errors: Array<
|
||||
ProcessDependenciesError | ProcessProjectGraphError
|
||||
>,
|
||||
public readonly partialProjectGraph: ProjectGraph
|
||||
) {
|
||||
super('Failed to create dependencies. See above for errors');
|
||||
this.name = this.constructor.name;
|
||||
}
|
||||
return updatedGraph;
|
||||
}
|
||||
|
||||
function readRootTsConfig() {
|
||||
@ -400,3 +396,52 @@ function readRootTsConfig() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export async function applyProjectMetadata(
|
||||
graph: ProjectGraph,
|
||||
plugins: LoadedNxPlugin[],
|
||||
context: CreateMetadataContext,
|
||||
sourceMap: ConfigurationSourceMaps
|
||||
): Promise<{ graph: ProjectGraph; errors?: CreateMetadataError[] }> {
|
||||
const results: { metadata: ProjectsMetadata; pluginName: string }[] = [];
|
||||
const errors: CreateMetadataError[] = [];
|
||||
|
||||
const promises = plugins.map(async (plugin) => {
|
||||
if (isNxPluginV2(plugin) && plugin.createMetadata) {
|
||||
performance.mark(`${plugin.name}:createMetadata - start`);
|
||||
try {
|
||||
const metadata = await plugin.createMetadata(graph, undefined, context);
|
||||
results.push({ metadata, pluginName: plugin.name });
|
||||
} catch (e) {
|
||||
errors.push(new CreateMetadataError(e, plugin.name));
|
||||
} finally {
|
||||
performance.mark(`${plugin.name}:createMetadata - end`);
|
||||
performance.measure(
|
||||
`${plugin.name}:createMetadata`,
|
||||
`${plugin.name}:createMetadata - start`,
|
||||
`${plugin.name}:createMetadata - end`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
for (const { metadata: projectsMetadata, pluginName } of results) {
|
||||
for (const project in projectsMetadata) {
|
||||
const projectConfiguration: ProjectConfiguration =
|
||||
graph.nodes[project]?.data;
|
||||
if (projectConfiguration) {
|
||||
projectConfiguration.metadata = mergeMetadata(
|
||||
sourceMap[project],
|
||||
[null, pluginName],
|
||||
'metadata',
|
||||
projectsMetadata[project].metadata,
|
||||
projectConfiguration.metadata
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { errors, graph };
|
||||
}
|
||||
|
||||
@ -4,16 +4,13 @@ import {
|
||||
ConfigurationSourceMaps,
|
||||
} from './utils/project-configuration-utils';
|
||||
import { ProjectConfiguration } from '../config/workspace-json-project-json';
|
||||
import {
|
||||
ProcessDependenciesError,
|
||||
ProcessProjectGraphError,
|
||||
} from './build-project-graph';
|
||||
import { ProjectGraph } from '../config/project-graph';
|
||||
|
||||
export class ProjectGraphError extends Error {
|
||||
readonly #errors: Array<
|
||||
| CreateNodesError
|
||||
| MergeNodesError
|
||||
| CreateMetadataError
|
||||
| ProjectsWithNoNameError
|
||||
| ProjectsWithConflictingNamesError
|
||||
| ProcessDependenciesError
|
||||
@ -30,6 +27,7 @@ export class ProjectGraphError extends Error {
|
||||
| ProjectsWithConflictingNamesError
|
||||
| ProcessDependenciesError
|
||||
| ProcessProjectGraphError
|
||||
| CreateMetadataError
|
||||
>,
|
||||
partialProjectGraph: ProjectGraph,
|
||||
partialSourceMaps: ConfigurationSourceMaps
|
||||
@ -198,6 +196,73 @@ export class MergeNodesError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export class CreateMetadataError extends Error {
|
||||
constructor(public readonly error: Error, public readonly plugin: string) {
|
||||
super(`The "${plugin}" plugin threw an error while creating metadata:`, {
|
||||
cause: error,
|
||||
});
|
||||
this.name = this.constructor.name;
|
||||
}
|
||||
}
|
||||
|
||||
export class ProcessDependenciesError extends Error {
|
||||
constructor(public readonly pluginName: string, { cause }) {
|
||||
super(
|
||||
`The "${pluginName}" plugin threw an error while creating dependencies:`,
|
||||
{
|
||||
cause,
|
||||
}
|
||||
);
|
||||
this.name = this.constructor.name;
|
||||
this.stack = `${this.message}\n ${cause.stack.split('\n').join('\n ')}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class ProcessProjectGraphError extends Error {
|
||||
constructor(public readonly pluginName: string, { cause }) {
|
||||
super(
|
||||
`The "${pluginName}" plugin threw an error while processing the project graph:`,
|
||||
{
|
||||
cause,
|
||||
}
|
||||
);
|
||||
this.name = this.constructor.name;
|
||||
this.stack = `${this.message}\n ${cause.stack.split('\n').join('\n ')}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class AggregateProjectGraphError extends Error {
|
||||
constructor(
|
||||
public readonly errors: Array<
|
||||
CreateMetadataError | ProcessDependenciesError | ProcessProjectGraphError
|
||||
>,
|
||||
public readonly partialProjectGraph: ProjectGraph
|
||||
) {
|
||||
super('Failed to create project graph. See above for errors');
|
||||
this.name = this.constructor.name;
|
||||
}
|
||||
}
|
||||
|
||||
export function isAggregateProjectGraphError(
|
||||
e: unknown
|
||||
): e is AggregateProjectGraphError {
|
||||
return (
|
||||
e instanceof AggregateProjectGraphError ||
|
||||
(typeof e === 'object' &&
|
||||
'name' in e &&
|
||||
e?.name === AggregateProjectGraphError.prototype.name)
|
||||
);
|
||||
}
|
||||
|
||||
export function isCreateMetadataError(e: unknown): e is CreateMetadataError {
|
||||
return (
|
||||
e instanceof CreateMetadataError ||
|
||||
(typeof e === 'object' &&
|
||||
'name' in e &&
|
||||
e?.name === CreateMetadataError.prototype.name)
|
||||
);
|
||||
}
|
||||
|
||||
export function isCreateNodesError(e: unknown): e is CreateNodesError {
|
||||
return (
|
||||
e instanceof CreateNodesError ||
|
||||
|
||||
@ -11,11 +11,16 @@ import { shouldMergeAngularProjects } from '../../adapter/angular-json';
|
||||
import {
|
||||
CreateDependencies,
|
||||
CreateDependenciesContext,
|
||||
CreateMetadata,
|
||||
CreateMetadataContext,
|
||||
CreateNodesContext,
|
||||
CreateNodesResult,
|
||||
NxPluginV2,
|
||||
} from './public-api';
|
||||
import { ProjectGraphProcessor } from '../../config/project-graph';
|
||||
import {
|
||||
ProjectGraph,
|
||||
ProjectGraphProcessor,
|
||||
} from '../../config/project-graph';
|
||||
import { runCreateNodesInParallel } from './utils';
|
||||
import { loadNxPluginInIsolation } from './isolation';
|
||||
import { loadNxPlugin, unregisterPluginTSTranspiler } from './loader';
|
||||
@ -34,6 +39,10 @@ export class LoadedNxPlugin {
|
||||
readonly createDependencies?: (
|
||||
context: CreateDependenciesContext
|
||||
) => ReturnType<CreateDependencies>;
|
||||
readonly createMetadata?: (
|
||||
graph: ProjectGraph,
|
||||
context: CreateMetadataContext
|
||||
) => ReturnType<CreateMetadata>;
|
||||
readonly processProjectGraph?: ProjectGraphProcessor;
|
||||
|
||||
readonly options?: unknown;
|
||||
@ -61,6 +70,11 @@ export class LoadedNxPlugin {
|
||||
plugin.createDependencies(this.options, context);
|
||||
}
|
||||
|
||||
if (plugin.createMetadata) {
|
||||
this.createMetadata = (graph, context) =>
|
||||
plugin.createMetadata(graph, this.options, context);
|
||||
}
|
||||
|
||||
this.processProjectGraph = plugin.processProjectGraph;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,11 @@ import {
|
||||
ProjectGraphProcessorContext,
|
||||
} from '../../../config/project-graph';
|
||||
import { PluginConfiguration } from '../../../config/nx-json';
|
||||
import { CreateDependenciesContext, CreateNodesContext } from '../public-api';
|
||||
import {
|
||||
CreateDependenciesContext,
|
||||
CreateMetadataContext,
|
||||
CreateNodesContext,
|
||||
} from '../public-api';
|
||||
import { LoadedNxPlugin } from '../internal-api';
|
||||
import { Serializable } from 'child_process';
|
||||
|
||||
@ -23,6 +27,7 @@ export interface PluginWorkerLoadResult {
|
||||
createNodesPattern: string;
|
||||
hasCreateDependencies: boolean;
|
||||
hasProcessProjectGraph: boolean;
|
||||
hasCreateMetadata: boolean;
|
||||
success: true;
|
||||
}
|
||||
| {
|
||||
@ -63,6 +68,15 @@ export interface PluginCreateDependenciesMessage {
|
||||
};
|
||||
}
|
||||
|
||||
export interface PluginCreateMetadataMessage {
|
||||
type: 'createMetadata';
|
||||
payload: {
|
||||
graph: ProjectGraph;
|
||||
context: CreateMetadataContext;
|
||||
tx: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PluginCreateDependenciesResult {
|
||||
type: 'createDependenciesResult';
|
||||
payload:
|
||||
@ -78,6 +92,21 @@ export interface PluginCreateDependenciesResult {
|
||||
};
|
||||
}
|
||||
|
||||
export interface PluginCreateMetadataResult {
|
||||
type: 'createMetadataResult';
|
||||
payload:
|
||||
| {
|
||||
metadata: ReturnType<LoadedNxPlugin['createMetadata']>;
|
||||
success: true;
|
||||
tx: string;
|
||||
}
|
||||
| {
|
||||
success: false;
|
||||
error: string;
|
||||
tx: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PluginWorkerProcessProjectGraphMessage {
|
||||
type: 'processProjectGraph';
|
||||
payload: {
|
||||
@ -106,13 +135,15 @@ export type PluginWorkerMessage =
|
||||
| PluginWorkerLoadMessage
|
||||
| PluginWorkerCreateNodesMessage
|
||||
| PluginCreateDependenciesMessage
|
||||
| PluginWorkerProcessProjectGraphMessage;
|
||||
| PluginWorkerProcessProjectGraphMessage
|
||||
| PluginCreateMetadataMessage;
|
||||
|
||||
export type PluginWorkerResult =
|
||||
| PluginWorkerLoadResult
|
||||
| PluginWorkerCreateNodesResult
|
||||
| PluginCreateDependenciesResult
|
||||
| PluginWorkerProcessProjectGraphResult;
|
||||
| PluginWorkerProcessProjectGraphResult
|
||||
| PluginCreateMetadataResult;
|
||||
|
||||
export function isPluginWorkerMessage(
|
||||
message: Serializable
|
||||
|
||||
@ -153,6 +153,18 @@ function createWorkerHandler(
|
||||
});
|
||||
}
|
||||
: undefined,
|
||||
createMetadata: result.hasCreateMetadata
|
||||
? (graph, ctx) => {
|
||||
const tx =
|
||||
pluginName + ':createMetadata:' + performance.now();
|
||||
return registerPendingPromise(tx, pending, () => {
|
||||
worker.send({
|
||||
type: 'createMetadata',
|
||||
payload: { graph, context: ctx, tx },
|
||||
});
|
||||
});
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
} else if (result.success === false) {
|
||||
onloadError(result.error);
|
||||
@ -182,6 +194,14 @@ function createWorkerHandler(
|
||||
rejector(result.error);
|
||||
}
|
||||
},
|
||||
createMetadataResult: ({ tx, ...result }) => {
|
||||
const { resolver, rejector } = pending.get(tx);
|
||||
if (result.success) {
|
||||
resolver(result.metadata);
|
||||
} else if (result.success === false) {
|
||||
rejector(result.error);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@ -27,6 +27,8 @@ process.on('message', async (message: Serializable) => {
|
||||
'createDependencies' in plugin && !!plugin.createDependencies,
|
||||
hasProcessProjectGraph:
|
||||
'processProjectGraph' in plugin && !!plugin.processProjectGraph,
|
||||
hasCreateMetadata:
|
||||
'createMetadata' in plugin && !!plugin.createMetadata,
|
||||
success: true,
|
||||
},
|
||||
};
|
||||
@ -94,5 +96,19 @@ process.on('message', async (message: Serializable) => {
|
||||
};
|
||||
}
|
||||
},
|
||||
createMetadata: async ({ graph, context, tx }) => {
|
||||
try {
|
||||
const result = await plugin.createMetadata(graph, context);
|
||||
return {
|
||||
type: 'createMetadataResult',
|
||||
payload: { metadata: result, success: true, tx },
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
type: 'createMetadataResult',
|
||||
payload: { success: false, error: e.stack, tx },
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@ -98,6 +98,22 @@ export type CreateDependencies<T = unknown> = (
|
||||
context: CreateDependenciesContext
|
||||
) => RawProjectGraphDependency[] | Promise<RawProjectGraphDependency[]>;
|
||||
|
||||
export type CreateMetadataContext = {
|
||||
readonly nxJsonConfiguration: NxJsonConfiguration;
|
||||
readonly workspaceRoot: string;
|
||||
};
|
||||
|
||||
export type ProjectsMetadata = Record<
|
||||
string,
|
||||
Pick<ProjectConfiguration, 'metadata'>
|
||||
>;
|
||||
|
||||
export type CreateMetadata<T = unknown> = (
|
||||
graph: ProjectGraph,
|
||||
options: T | undefined,
|
||||
context: CreateMetadataContext
|
||||
) => ProjectsMetadata | Promise<ProjectsMetadata>;
|
||||
|
||||
/**
|
||||
* A plugin for Nx which creates nodes and dependencies for the {@link ProjectGraph}
|
||||
*/
|
||||
@ -110,11 +126,15 @@ export type NxPluginV2<TOptions = unknown> = {
|
||||
*/
|
||||
createNodes?: CreateNodes<TOptions>;
|
||||
|
||||
// Todo(@AgentEnder): This shouldn't be a full processor, since its only responsible for defining edges between projects. What do we want the API to be?
|
||||
/**
|
||||
* Provides a function to analyze files to create dependencies for the {@link ProjectGraph}
|
||||
*/
|
||||
createDependencies?: CreateDependencies<TOptions>;
|
||||
|
||||
/**
|
||||
* Provides a function to create metadata for the {@link ProjectGraph}
|
||||
*/
|
||||
createMetadata?: CreateMetadata<TOptions>;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { performance } from 'perf_hooks';
|
||||
|
||||
import { readNxJson } from '../config/nx-json';
|
||||
import { ProjectGraph } from '../config/project-graph';
|
||||
import {
|
||||
@ -11,17 +12,18 @@ import { fileExists } from '../utils/fileutils';
|
||||
import { output } from '../utils/output';
|
||||
import { stripIndents } from '../utils/strip-indents';
|
||||
import { workspaceRoot } from '../utils/workspace-root';
|
||||
import { buildProjectGraphUsingProjectFileMap } from './build-project-graph';
|
||||
import {
|
||||
CreateDependenciesError,
|
||||
buildProjectGraphUsingProjectFileMap,
|
||||
} from './build-project-graph';
|
||||
AggregateProjectGraphError,
|
||||
isAggregateProjectGraphError,
|
||||
ProjectConfigurationsError,
|
||||
ProjectGraphError,
|
||||
} from './error-types';
|
||||
import {
|
||||
readFileMapCache,
|
||||
readProjectGraphCache,
|
||||
writeCache,
|
||||
} from './nx-deps-cache';
|
||||
|
||||
import { ProjectConfigurationsError, ProjectGraphError } from './error-types';
|
||||
import { loadNxPlugins } from './plugins/internal-api';
|
||||
import { ConfigurationResult } from './utils/project-configuration-utils';
|
||||
import {
|
||||
@ -120,7 +122,7 @@ export async function buildProjectGraphAndSourceMapsWithoutDaemon() {
|
||||
|
||||
const cacheEnabled = process.env.NX_CACHE_PROJECT_GRAPH !== 'false';
|
||||
performance.mark('build-project-graph-using-project-file-map:start');
|
||||
let createDependenciesError: CreateDependenciesError;
|
||||
let projectGraphError: AggregateProjectGraphError;
|
||||
let projectGraphResult: Awaited<
|
||||
ReturnType<typeof buildProjectGraphUsingProjectFileMap>
|
||||
>;
|
||||
@ -132,15 +134,16 @@ export async function buildProjectGraphAndSourceMapsWithoutDaemon() {
|
||||
allWorkspaceFiles,
|
||||
rustReferences,
|
||||
cacheEnabled ? readFileMapCache() : null,
|
||||
plugins
|
||||
plugins,
|
||||
sourceMaps
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof CreateDependenciesError) {
|
||||
if (isAggregateProjectGraphError(e)) {
|
||||
projectGraphResult = {
|
||||
projectGraph: e.partialProjectGraph,
|
||||
projectFileMapCache: null,
|
||||
};
|
||||
createDependenciesError = e;
|
||||
projectGraphError = e;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
@ -155,7 +158,7 @@ export async function buildProjectGraphAndSourceMapsWithoutDaemon() {
|
||||
|
||||
const errors = [
|
||||
...(projectConfigurationsError?.errors ?? []),
|
||||
...(createDependenciesError?.errors ?? []),
|
||||
...(projectGraphError?.errors ?? []),
|
||||
];
|
||||
|
||||
if (errors.length > 0) {
|
||||
|
||||
@ -32,7 +32,7 @@ import {
|
||||
isProjectsWithNoNameError,
|
||||
} from '../error-types';
|
||||
|
||||
export type SourceInformation = [file: string, plugin: string];
|
||||
export type SourceInformation = [file: string | null, plugin: string];
|
||||
export type ConfigurationSourceMaps = Record<
|
||||
string,
|
||||
Record<string, SourceInformation>
|
||||
@ -224,7 +224,7 @@ export function mergeProjectConfigurationIntoRootMap(
|
||||
updatedProjectConfiguration;
|
||||
}
|
||||
|
||||
function mergeMetadata<T = ProjectMetadata | TargetMetadata>(
|
||||
export function mergeMetadata<T = ProjectMetadata | TargetMetadata>(
|
||||
sourceMap: Record<string, [file: string, plugin: string]>,
|
||||
sourceInformation: [file: string, plugin: string],
|
||||
baseSourceMapPath: string,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user