0
# File Operations
1
2
File system utilities and virtual file tree operations for generators, code transformations, and automated workspace modifications.
3
4
## Capabilities
5
6
### JSON File Operations
7
8
High-level utilities for reading, writing, and updating JSON files with type safety.
9
10
```typescript { .api }
11
/**
12
* Reads and parses a JSON file
13
* @param path - Absolute or workspace-relative path to JSON file
14
* @returns Parsed JSON object
15
*/
16
function readJsonFile<T = any>(path: string): T;
17
18
/**
19
* Writes an object to a JSON file with formatting
20
* @param path - Absolute or workspace-relative path to JSON file
21
* @param data - Object to serialize as JSON
22
*/
23
function writeJsonFile<T = any>(path: string, data: T): void;
24
25
/**
26
* Updates a JSON file using an updater function
27
* @param path - Absolute or workspace-relative path to JSON file
28
* @param updater - Function that receives current data and returns updated data
29
*/
30
function updateJsonFile<T = any>(path: string, updater: (data: T) => T): void;
31
32
/**
33
* Reads and parses a JSON file from virtual tree
34
* @param tree - Virtual file tree
35
* @param path - Path to JSON file
36
* @returns Parsed JSON object
37
*/
38
function readJson<T = any>(tree: Tree, path: string): T;
39
40
/**
41
* Writes an object to a JSON file in virtual tree
42
* @param tree - Virtual file tree
43
* @param path - Path to JSON file
44
* @param data - Object to serialize as JSON
45
*/
46
function writeJson<T = any>(tree: Tree, path: string, data: T): void;
47
48
/**
49
* Updates a JSON file in virtual tree using an updater function
50
* @param tree - Virtual file tree
51
* @param path - Path to JSON file
52
* @param updater - Function that receives current data and returns updated data
53
*/
54
function updateJson<T = any>(tree: Tree, path: string, updater: (data: T) => T): void;
55
```
56
57
### Virtual File Tree
58
59
Virtual file system for generators that allows batch operations before writing to disk.
60
61
```typescript { .api }
62
/**
63
* Virtual file system interface for generators
64
*/
65
interface Tree {
66
/** Read file contents as buffer */
67
read(filePath: string): Buffer | null;
68
/** Read file contents as string with optional encoding */
69
read(filePath: string, encoding: BufferEncoding): string | null;
70
/** Write file contents from buffer or string */
71
write(filePath: string, content: Buffer | string): void;
72
/** Check if file exists */
73
exists(filePath: string): boolean;
74
/** Delete file */
75
delete(filePath: string): void;
76
/** Rename file */
77
rename(from: string, to: string): void;
78
/** Check if path is a file */
79
isFile(filePath: string): boolean;
80
/** List children of directory */
81
children(dirPath: string): string[];
82
/** Get file change type */
83
listChanges(): FileChange[];
84
}
85
86
interface FileChange {
87
path: string;
88
type: 'CREATE' | 'DELETE' | 'UPDATE';
89
content?: Buffer | string;
90
}
91
92
/**
93
* Creates a virtual file tree from the current file system
94
* @param root - Root directory path (defaults to workspace root)
95
* @returns Virtual file tree
96
*/
97
function createFsTree(root?: string): FsTree;
98
99
/**
100
* File system-based tree implementation
101
*/
102
declare class FsTree implements Tree {
103
constructor(root: string, isVerbose: boolean);
104
105
read(filePath: string): Buffer | null;
106
read(filePath: string, encoding: BufferEncoding): string | null;
107
write(filePath: string, content: Buffer | string): void;
108
exists(filePath: string): boolean;
109
delete(filePath: string): void;
110
rename(from: string, to: string): void;
111
isFile(filePath: string): boolean;
112
children(dirPath: string): string[];
113
listChanges(): FileChange[];
114
}
115
```
116
117
### File Tree Operations
118
119
Functions for applying changes from virtual trees to the actual file system.
120
121
```typescript { .api }
122
/**
123
* Applies all changes from a virtual tree to the file system
124
* @param tree - Virtual file tree with pending changes
125
* @param verbose - Whether to log changes being applied
126
*/
127
function flushChanges(tree: Tree, verbose?: boolean): void;
128
129
/**
130
* Applies string changes to content
131
* @param content - Original string content
132
* @param changes - Array of string insertions and deletions
133
* @returns Modified string content
134
*/
135
function applyChangesToString(
136
content: string,
137
changes: (StringInsertion | StringDeletion)[]
138
): string;
139
140
interface StringInsertion {
141
type: 'insertion';
142
index: number;
143
text: string;
144
}
145
146
interface StringDeletion {
147
type: 'deletion';
148
start: number;
149
length: number;
150
}
151
```
152
153
### Path Utilities
154
155
Utilities for working with file paths in a cross-platform manner.
156
157
```typescript { .api }
158
/**
159
* Joins path fragments using the correct separator
160
* @param fragments - Path fragments to join
161
* @returns Joined path string
162
*/
163
function joinPathFragments(...fragments: string[]): string;
164
165
/**
166
* Normalizes a path by resolving . and .. segments
167
* @param path - Path to normalize
168
* @returns Normalized path string
169
*/
170
function normalizePath(path: string): string;
171
172
/**
173
* Gets the relative path from one path to another
174
* @param from - Source path
175
* @param to - Target path
176
* @returns Relative path string
177
*/
178
function getRelativePath(from: string, to: string): string;
179
180
/**
181
* Converts Windows-style paths to Unix-style paths
182
* @param path - Path to convert
183
* @returns Unix-style path
184
*/
185
function toUnixPath(path: string): string;
186
```
187
188
### Template Processing
189
190
Functions for processing file templates with variable substitution.
191
192
```typescript { .api }
193
/**
194
* Generates files from templates with variable substitution
195
* @param tree - Virtual file tree
196
* @param templatePath - Path to template directory
197
* @param targetPath - Target directory for generated files
198
* @param substitutions - Variables for template substitution
199
*/
200
function generateFiles(
201
tree: Tree,
202
templatePath: string,
203
targetPath: string,
204
substitutions: any
205
): void;
206
207
/**
208
* Offset from template file names for substitution patterns
209
* @param name - Template file name
210
* @param substitutions - Variables for substitution
211
* @returns Processed file name
212
*/
213
function offsetFromRoot(name: string): string;
214
215
/**
216
* Names utility functions for template processing
217
*/
218
interface Names {
219
/** Convert to className format */
220
className: (name: string) => string;
221
/** Convert to propertyName format */
222
propertyName: (name: string) => string;
223
/** Convert to constantName format */
224
constantName: (name: string) => string;
225
/** Convert to fileName format */
226
fileName: (name: string) => string;
227
}
228
229
const names: Names;
230
```
231
232
### File Filtering and Visiting
233
234
Utilities for traversing and filtering files in the workspace.
235
236
```typescript { .api }
237
/**
238
* Visits files that are not ignored by .gitignore
239
* @param tree - Virtual file tree
240
* @param dirPath - Directory to start from (default: workspace root)
241
* @param visitor - Function called for each file
242
*/
243
function visitNotIgnoredFiles(
244
tree: Tree,
245
dirPath: string,
246
visitor: (filePath: string) => void
247
): void;
248
249
/**
250
* Gets all files matching a glob pattern
251
* @param tree - Virtual file tree
252
* @param pattern - Glob pattern to match
253
* @returns Array of matching file paths
254
*/
255
function getMatchingFiles(tree: Tree, pattern: string): string[];
256
257
/**
258
* Checks if a file should be ignored based on .gitignore rules
259
* @param filePath - Path to check
260
* @param root - Root directory for .gitignore lookup
261
* @returns True if file should be ignored
262
*/
263
function isIgnored(filePath: string, root?: string): boolean;
264
```
265
266
## Usage Examples
267
268
### Basic File Operations
269
270
```typescript
271
import {
272
readJsonFile,
273
writeJsonFile,
274
updateJsonFile,
275
logger
276
} from "nx/src/devkit-exports";
277
278
// Read package.json
279
const packageJson = readJsonFile('package.json');
280
logger.info(`Project: ${packageJson.name} v${packageJson.version}`);
281
282
// Update package.json
283
updateJsonFile('package.json', (json) => ({
284
...json,
285
scripts: {
286
...json.scripts,
287
'custom-build': 'nx build --configuration=production'
288
}
289
}));
290
291
// Write new configuration file
292
writeJsonFile('nx-custom.json', {
293
customSetting: 'value',
294
options: {
295
enableFeature: true
296
}
297
});
298
```
299
300
### Generator File Operations
301
302
```typescript
303
import {
304
Tree,
305
generateFiles,
306
formatFiles,
307
joinPathFragments,
308
names
309
} from "nx/src/devkit-exports";
310
311
interface ComponentGeneratorSchema {
312
name: string;
313
project: string;
314
directory?: string;
315
}
316
317
export default async function componentGenerator(
318
tree: Tree,
319
options: ComponentGeneratorSchema
320
) {
321
const projectRoot = readProjectConfiguration(tree, options.project).root;
322
const componentDir = options.directory
323
? joinPathFragments(projectRoot, 'src', options.directory)
324
: joinPathFragments(projectRoot, 'src');
325
326
// Generate component files from templates
327
generateFiles(
328
tree,
329
joinPathFragments(__dirname, 'files'),
330
componentDir,
331
{
332
...options,
333
...names(options.name),
334
template: '' // Remove __template__ from file names
335
}
336
);
337
338
// Update barrel export
339
const indexPath = joinPathFragments(componentDir, 'index.ts');
340
const componentName = names(options.name).className;
341
342
if (tree.exists(indexPath)) {
343
const content = tree.read(indexPath, 'utf-8');
344
const updatedContent = `${content}\nexport * from './${names(options.name).fileName}';`;
345
tree.write(indexPath, updatedContent);
346
} else {
347
tree.write(indexPath, `export * from './${names(options.name).fileName}';`);
348
}
349
350
// Format generated files
351
await formatFiles(tree);
352
}
353
```
354
355
### Advanced Tree Operations
356
357
```typescript
358
import {
359
Tree,
360
visitNotIgnoredFiles,
361
applyChangesToString,
362
StringInsertion
363
} from "nx/src/devkit-exports";
364
365
function addImportToFiles(tree: Tree, importStatement: string) {
366
visitNotIgnoredFiles(tree, '', (filePath) => {
367
if (!filePath.endsWith('.ts') && !filePath.endsWith('.tsx')) {
368
return;
369
}
370
371
const content = tree.read(filePath, 'utf-8');
372
if (!content || content.includes(importStatement)) {
373
return;
374
}
375
376
// Add import at the top of the file
377
const changes: StringInsertion[] = [{
378
type: 'insertion',
379
index: 0,
380
text: `${importStatement}\n`
381
}];
382
383
const updatedContent = applyChangesToString(content, changes);
384
tree.write(filePath, updatedContent);
385
});
386
}
387
388
// Usage: Add import to all TypeScript files
389
addImportToFiles(tree, "import { logger } from 'nx/src/devkit-exports';");
390
```
391
392
### Template File Structure
393
394
Template files use substitution patterns in file names and content:
395
396
```
397
files/
398
├── __name@fileName__.component.ts__template__
399
├── __name@fileName__.component.spec.ts__template__
400
└── __name@fileName__.module.ts__template__
401
```
402
403
Template content example (`__name@fileName__.component.ts__template__`):
404
405
```typescript
406
import { Component } from '@angular/core';
407
408
@Component({
409
selector: '<%= selector %>',
410
templateUrl: './<%= fileName %>.component.html',
411
styleUrls: ['./<%= fileName %>.component.css']
412
})
413
export class <%= className %>Component {
414
title = '<%= name %>';
415
}
416
```
417
418
### Working with Configuration Files
419
420
```typescript
421
import { Tree, updateJsonFile, readProjectConfiguration } from "nx/src/devkit-exports";
422
423
function updateProjectTsConfig(tree: Tree, projectName: string, options: any) {
424
const projectConfig = readProjectConfiguration(tree, projectName);
425
const tsConfigPath = joinPathFragments(projectConfig.root, 'tsconfig.json');
426
427
if (tree.exists(tsConfigPath)) {
428
updateJsonFile(tree, tsConfigPath, (tsconfig) => ({
429
...tsconfig,
430
compilerOptions: {
431
...tsconfig.compilerOptions,
432
...options.compilerOptions
433
}
434
}));
435
}
436
}
437
```