0
# Plugin Development
1
2
Comprehensive plugin system for extending Nx with custom node creation, dependency analysis, and metadata generation. Plugins allow you to integrate new tools and technologies into Nx workspaces by defining how they should be discovered, configured, and executed.
3
4
## Capabilities
5
6
### Plugin Interfaces
7
8
Core interfaces for creating Nx plugins.
9
10
```typescript { .api }
11
/**
12
* Modern Nx plugin interface (recommended)
13
*/
14
interface NxPluginV2 {
15
/** Plugin name */
16
name: string;
17
/** Function to create project nodes from configuration files */
18
createNodes?: CreateNodesV2;
19
/** Function to create dependencies between projects */
20
createDependencies?: CreateDependencies;
21
/** Function to create metadata for projects */
22
createMetadata?: CreateMetadata;
23
}
24
25
/**
26
* Union type of all supported plugin versions
27
*/
28
type NxPlugin = NxPluginV1 | NxPluginV2;
29
30
/**
31
* Legacy plugin interface (deprecated)
32
*/
33
interface NxPluginV1 {
34
name: string;
35
processProjectGraph?(
36
graph: ProjectGraph,
37
context: ProjectGraphProcessorContext
38
): ProjectGraph;
39
projectFilePatterns?: string[];
40
}
41
```
42
43
**Usage Examples:**
44
45
```typescript
46
import { NxPluginV2 } from "@nrwl/devkit";
47
48
const myPlugin: NxPluginV2 = {
49
name: 'my-custom-plugin',
50
createNodes: [
51
'**/my-config.json',
52
(configFiles, options, context) => {
53
// Create project nodes from configuration files
54
const results = {};
55
for (const configFile of configFiles) {
56
// Process each config file and create project nodes
57
}
58
return Promise.resolve(results);
59
}
60
],
61
createDependencies: (options, context) => {
62
// Analyze files and create project dependencies
63
return [];
64
}
65
};
66
67
export default myPlugin;
68
```
69
70
### CreateNodes API
71
72
Interface for creating project nodes from configuration files.
73
74
```typescript { .api }
75
/**
76
* Modern create nodes API (recommended)
77
*/
78
type CreateNodesV2<T = any> = [
79
/** Glob pattern for configuration files */
80
projectFilePattern: string,
81
/** Function to process matched files */
82
createNodesFunction: CreateNodesFunctionV2<T>
83
];
84
85
/**
86
* Function to create nodes from multiple configuration files
87
*/
88
type CreateNodesFunctionV2<T = any> = (
89
/** Array of matched configuration file paths */
90
projectConfigurationFiles: string[],
91
/** Plugin options */
92
options: T | undefined,
93
/** Creation context */
94
context: CreateNodesContextV2
95
) => Promise<CreateNodesResultV2>;
96
97
/**
98
* Context provided to create nodes functions
99
*/
100
interface CreateNodesContextV2 {
101
/** Workspace root directory */
102
workspaceRoot: string;
103
/** Nx JSON configuration */
104
nxJsonConfiguration: NxJsonConfiguration;
105
/** Configuration cache for performance */
106
configFiles?: Record<string, any>;
107
}
108
109
/**
110
* Result of creating nodes from configuration files
111
*/
112
interface CreateNodesResultV2 {
113
/** Created project configurations */
114
projects?: Record<string, ProjectConfiguration>;
115
/** Created external nodes */
116
externalNodes?: Record<string, ProjectGraphExternalNode>;
117
}
118
119
/**
120
* Legacy create nodes API (deprecated)
121
*/
122
type CreateNodes<T = any> = [
123
projectFilePattern: string,
124
createNodesFunction: CreateNodesFunction<T>
125
];
126
127
type CreateNodesFunction<T = any> = (
128
projectConfigurationFile: string,
129
options: T | undefined,
130
context: CreateNodesContext
131
) => CreateNodesResult | Promise<CreateNodesResult>;
132
```
133
134
**Usage Examples:**
135
136
```typescript
137
import {
138
CreateNodesV2,
139
CreateNodesFunctionV2,
140
ProjectConfiguration
141
} from "@nrwl/devkit";
142
143
// Example: Plugin that creates projects from package.json files
144
const createNodesFunction: CreateNodesFunctionV2 = async (
145
configFiles,
146
options,
147
context
148
) => {
149
const projects: Record<string, ProjectConfiguration> = {};
150
151
for (const configFile of configFiles) {
152
const packageJsonPath = configFile;
153
const packageJson = JSON.parse(
154
readFileSync(join(context.workspaceRoot, packageJsonPath), 'utf-8')
155
);
156
157
const projectRoot = dirname(packageJsonPath);
158
const projectName = packageJson.name || basename(projectRoot);
159
160
projects[projectRoot] = {
161
name: projectName,
162
root: projectRoot,
163
projectType: packageJson.private ? 'library' : 'application',
164
targets: {
165
build: {
166
executor: 'nx:run-commands',
167
options: {
168
command: 'npm run build',
169
cwd: projectRoot
170
}
171
}
172
}
173
};
174
}
175
176
return { projects };
177
};
178
179
export const createNodes: CreateNodesV2 = [
180
'**/package.json',
181
createNodesFunction
182
];
183
```
184
185
### CreateDependencies API
186
187
Interface for analyzing files and creating project dependencies.
188
189
```typescript { .api }
190
/**
191
* Function to create dependencies between projects
192
*/
193
type CreateDependencies<T = any> = (
194
/** Plugin options */
195
options: T | undefined,
196
/** Dependency creation context */
197
context: CreateDependenciesContext
198
) => RawProjectGraphDependency[] | Promise<RawProjectGraphDependency[]>;
199
200
/**
201
* Context provided to dependency creation functions
202
*/
203
interface CreateDependenciesContext {
204
/** Current project graph */
205
projects: Record<string, ProjectConfiguration>;
206
/** External nodes in the graph */
207
externalNodes: Record<string, ProjectGraphExternalNode>;
208
/** File map for the workspace */
209
fileMap: ProjectFileMap;
210
/** Files that have changed */
211
filesToProcess: FileData[];
212
/** Workspace root directory */
213
workspaceRoot: string;
214
/** Nx JSON configuration */
215
nxJsonConfiguration: NxJsonConfiguration;
216
}
217
218
/**
219
* Raw dependency representation
220
*/
221
interface RawProjectGraphDependency {
222
/** Source project name */
223
source: string;
224
/** Target project name */
225
target: string;
226
/** Type of dependency */
227
type: DependencyType;
228
/** Source file where dependency was found */
229
sourceFile?: string;
230
}
231
```
232
233
**Usage Examples:**
234
235
```typescript
236
import {
237
CreateDependencies,
238
RawProjectGraphDependency,
239
DependencyType
240
} from "@nrwl/devkit";
241
242
// Example: Plugin that finds import dependencies in TypeScript files
243
const createDependencies: CreateDependencies = (options, context) => {
244
const dependencies: RawProjectGraphDependency[] = [];
245
246
for (const [projectName, files] of Object.entries(context.fileMap)) {
247
for (const file of files) {
248
if (file.file.endsWith('.ts') || file.file.endsWith('.tsx')) {
249
const content = readFileSync(
250
join(context.workspaceRoot, file.file),
251
'utf-8'
252
);
253
254
// Simple regex to find imports (use proper parser in real implementation)
255
const importMatches = content.match(/from ['"]([^'"]+)['"]/g) || [];
256
257
for (const match of importMatches) {
258
const importPath = match.match(/from ['"]([^'"]+)['"]/)?.[1];
259
if (importPath?.startsWith('@myorg/')) {
260
const targetProject = importPath.replace('@myorg/', '');
261
if (context.projects[targetProject]) {
262
dependencies.push({
263
source: projectName,
264
target: targetProject,
265
type: DependencyType.static,
266
sourceFile: file.file
267
});
268
}
269
}
270
}
271
}
272
}
273
}
274
275
return dependencies;
276
};
277
```
278
279
### CreateMetadata API
280
281
Interface for generating metadata for projects in the graph.
282
283
```typescript { .api }
284
/**
285
* Function to create metadata for projects
286
*/
287
type CreateMetadata<T = any> = (
288
/** Plugin options */
289
options: T | undefined,
290
/** Metadata creation context */
291
context: CreateMetadataContext
292
) => Promise<ProjectsMetadata>;
293
294
/**
295
* Context provided to metadata creation functions
296
*/
297
interface CreateMetadataContext {
298
/** Current project graph */
299
projectGraph: ProjectGraph;
300
/** Nx JSON configuration */
301
nxJsonConfiguration: NxJsonConfiguration;
302
/** Workspace root directory */
303
workspaceRoot: string;
304
}
305
306
/**
307
* Map of project metadata
308
*/
309
type ProjectsMetadata = Record<string, Record<string, any>>;
310
```
311
312
**Usage Examples:**
313
314
```typescript
315
import { CreateMetadata, ProjectsMetadata } from "@nrwl/devkit";
316
317
// Example: Plugin that adds build metadata to projects
318
const createMetadata: CreateMetadata = async (options, context) => {
319
const metadata: ProjectsMetadata = {};
320
321
for (const [projectName, node] of Object.entries(context.projectGraph.nodes)) {
322
if (node.type !== 'npm') {
323
const projectRoot = node.data.root;
324
const packageJsonPath = join(context.workspaceRoot, projectRoot, 'package.json');
325
326
if (existsSync(packageJsonPath)) {
327
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
328
329
metadata[projectName] = {
330
packageVersion: packageJson.version,
331
hasTests: !!packageJson.scripts?.test,
332
dependencies: Object.keys(packageJson.dependencies || {}),
333
devDependencies: Object.keys(packageJson.devDependencies || {})
334
};
335
}
336
}
337
}
338
339
return metadata;
340
};
341
```
342
343
### Plugin Utilities
344
345
Utility functions for working with plugins.
346
347
```typescript { .api }
348
/**
349
* Create nodes from a list of configuration files
350
* @param configFiles - Array of configuration file paths
351
* @param createNodes - Create nodes configuration
352
* @param options - Plugin options
353
* @param context - Creation context
354
* @returns Promise resolving to created nodes
355
*/
356
function createNodesFromFiles<T = any>(
357
configFiles: string[],
358
createNodes: CreateNodesV2<T>,
359
options: T,
360
context: CreateNodesContextV2
361
): Promise<CreateNodesResultV2>;
362
363
/**
364
* Error aggregation for plugin operations
365
*/
366
class AggregateCreateNodesError extends Error {
367
constructor(
368
public readonly errors: Array<[file: string, error: Error]>,
369
public readonly partialResults: CreateNodesResultV2
370
) {
371
super('Multiple errors occurred while creating nodes');
372
}
373
}
374
```
375
376
**Usage Examples:**
377
378
```typescript
379
import {
380
createNodesFromFiles,
381
AggregateCreateNodesError
382
} from "@nrwl/devkit";
383
384
async function processConfigFiles(
385
configFiles: string[],
386
createNodes: CreateNodesV2,
387
options: any,
388
context: CreateNodesContextV2
389
) {
390
try {
391
const result = await createNodesFromFiles(
392
configFiles,
393
createNodes,
394
options,
395
context
396
);
397
398
return result;
399
} catch (error) {
400
if (error instanceof AggregateCreateNodesError) {
401
console.error('Errors occurred:', error.errors);
402
// Use partial results if available
403
return error.partialResults;
404
}
405
throw error;
406
}
407
}
408
```
409
410
### Plugin Configuration
411
412
Types for configuring plugins in nx.json.
413
414
```typescript { .api }
415
/**
416
* Plugin configuration in nx.json
417
*/
418
interface PluginConfiguration {
419
/** Plugin package name or path */
420
plugin: string;
421
/** Plugin-specific options */
422
options?: any;
423
/** Exclude patterns */
424
exclude?: string[];
425
/** Include patterns */
426
include?: string[];
427
}
428
429
/**
430
* Expanded plugin configuration with resolved information
431
*/
432
interface ExpandedPluginConfiguration extends PluginConfiguration {
433
/** Resolved plugin name */
434
name: string;
435
/** Whether plugin creates nodes */
436
createNodes?: [string, CreateNodesFunctionV2];
437
/** Whether plugin creates dependencies */
438
createDependencies?: CreateDependencies;
439
/** Whether plugin creates metadata */
440
createMetadata?: CreateMetadata;
441
}
442
```
443
444
**Usage Examples:**
445
446
```typescript
447
// nx.json plugin configuration
448
{
449
"plugins": [
450
{
451
"plugin": "@my-org/nx-plugin",
452
"options": {
453
"buildCommand": "npm run build"
454
},
455
"exclude": ["**/node_modules/**"]
456
}
457
]
458
}
459
```