0
# File System Operations
1
2
Memory filesystem operations, conflict resolution, and file transforms for generator output management and package installation tasks.
3
4
## Capabilities
5
6
### Commit Shared FileSystem
7
8
Commits the memory filesystem to disk with conflict resolution and transforms.
9
10
```typescript { .api }
11
/**
12
* Commits the MemFs to disc with conflict resolution
13
* @param options - Configuration for commit operation
14
*/
15
function commitSharedFsTask(options: {
16
/** I/O adapter for user interaction */
17
adapter: InputOutputAdapter;
18
/** Options for conflict resolution */
19
conflicterOptions?: ConflicterOptions;
20
/** Memory filesystem store to commit */
21
sharedFs: Store<MemFsEditorFile>;
22
}): Promise<void>;
23
```
24
25
**Usage Example:**
26
27
```typescript
28
import { commitSharedFsTask } from "yeoman-environment";
29
import { create as createMemFs } from "mem-fs";
30
import { QueuedAdapter } from "@yeoman/adapter";
31
32
// Create memory filesystem and adapter
33
const memFs = createMemFs();
34
const adapter = new QueuedAdapter();
35
36
// Commit filesystem with conflict resolution
37
await commitSharedFsTask({
38
adapter,
39
sharedFs: memFs,
40
conflicterOptions: {
41
force: false,
42
bail: false,
43
whitespace: true
44
}
45
});
46
```
47
48
### Package Manager Installation
49
50
Execute package manager install with auto-detection and error handling.
51
52
```typescript { .api }
53
/**
54
* Executes package manager install with detection and error handling
55
* @param options - Configuration for package manager installation
56
* @returns Promise resolving to true if installation succeeded
57
*/
58
function packageManagerInstallTask(options: PackageManagerInstallTaskOptions): Promise<boolean>;
59
60
interface PackageManagerInstallTaskOptions {
61
/** Memory filesystem store */
62
memFs: Store<MemFsEditorFile>;
63
/** Path to directory containing package.json */
64
packageJsonLocation: string;
65
/** I/O adapter for user interaction */
66
adapter: InputOutputAdapter;
67
/** Preferred package manager (npm, yarn, pnpm) */
68
nodePackageManager?: string;
69
/** Custom install task function or boolean to disable */
70
customInstallTask?: boolean | ((nodePackageManager: string | undefined, defaultTask: () => Promise<boolean>) => void | Promise<void>);
71
/** Skip installation entirely */
72
skipInstall?: boolean;
73
}
74
```
75
76
**Usage Examples:**
77
78
```typescript
79
import { packageManagerInstallTask } from "yeoman-environment";
80
import { create as createMemFs } from "mem-fs";
81
import { QueuedAdapter } from "@yeoman/adapter";
82
83
const memFs = createMemFs();
84
const adapter = new QueuedAdapter();
85
86
// Basic package manager install
87
const success = await packageManagerInstallTask({
88
memFs,
89
packageJsonLocation: "/project/path",
90
adapter
91
});
92
93
// With preferred package manager
94
await packageManagerInstallTask({
95
memFs,
96
packageJsonLocation: "/project/path",
97
adapter,
98
nodePackageManager: "yarn"
99
});
100
101
// With custom install task
102
await packageManagerInstallTask({
103
memFs,
104
packageJsonLocation: "/project/path",
105
adapter,
106
customInstallTask: async (packageManager, defaultTask) => {
107
console.log(`Installing with ${packageManager}...`);
108
await defaultTask();
109
console.log("Installation complete!");
110
}
111
});
112
113
// Skip installation
114
await packageManagerInstallTask({
115
memFs,
116
packageJsonLocation: "/project/path",
117
adapter,
118
skipInstall: true
119
});
120
```
121
122
### Environment FileSystem Integration
123
124
File system methods available on Environment instances for generator operations.
125
126
```typescript { .api }
127
/**
128
* Watch for package manager install operations
129
* Available on Environment and EnvironmentBase instances
130
* @param options - Watch configuration options
131
*/
132
watchForPackageManagerInstall(options?: any): void;
133
134
/**
135
* Apply transforms to filesystem
136
* Available on Environment and EnvironmentBase instances
137
* @param transformStreams - Transform streams to apply
138
* @param options - Transform options
139
*/
140
applyTransforms(transformStreams: any[], options?: any): Promise<void>;
141
142
/**
143
* Install local generator packages
144
* Available on Environment instances
145
* @param packages - Map of package names to versions
146
* @returns True if installation succeeded
147
*/
148
installLocalGenerators(packages: Record<string, string | undefined>): Promise<boolean>;
149
```
150
151
**Usage Examples:**
152
153
```typescript
154
import { createEnv } from "yeoman-environment";
155
156
const env = createEnv();
157
158
// Watch for package manager operations
159
env.watchForPackageManagerInstall({
160
detectExisting: true,
161
watchGlob: "**/package.json"
162
});
163
164
// Apply file transforms
165
await env.applyTransforms([
166
myTransformStream,
167
anotherTransform
168
], {
169
filter: (file) => file.path.endsWith('.js')
170
});
171
172
// Install local generators
173
const installed = await env.installLocalGenerators({
174
"generator-webapp": "^4.0.1",
175
"generator-node": "latest"
176
});
177
```
178
179
## Conflict Resolution
180
181
The commit process includes sophisticated conflict resolution:
182
183
### Conflict Resolution Options
184
185
```typescript { .api }
186
interface ConflicterOptions {
187
/** Override all existing files without prompting */
188
force?: boolean;
189
/** Fail immediately on first file conflict */
190
bail?: boolean;
191
/** Treat whitespace-only changes as non-conflicts */
192
whitespace?: boolean;
193
/** Skip conflict resolution for .yo-resolve files */
194
skipYoResolve?: boolean;
195
/** Custom conflict resolution adapter */
196
adapter?: InputOutputAdapter;
197
/** Dry run mode - show conflicts without applying */
198
dryRun?: boolean;
199
}
200
```
201
202
### Conflict Resolution Process
203
204
1. **File Scanning**: Identify files that will be modified
205
2. **Existing File Check**: Compare with existing files on disk
206
3. **Conflict Detection**: Detect content differences
207
4. **User Interaction**: Prompt for resolution strategy (if not forced)
208
5. **Resolution Application**: Apply chosen resolution
209
6. **Transform Pipeline**: Apply any configured transforms
210
7. **Disk Commit**: Write final files to disk
211
212
**Conflict Resolution Strategies:**
213
- **Skip**: Keep existing file, don't write new content
214
- **Force**: Overwrite existing file with new content
215
- **Merge**: Attempt automatic merge (if possible)
216
- **Manual**: Open editor for manual conflict resolution
217
218
### File Transform Pipeline
219
220
The commit process supports transform streams for file processing:
221
222
```typescript
223
import { transform } from "@yeoman/transform";
224
225
// Example transform usage
226
await env.applyTransforms([
227
// Prettier formatting
228
transform.prettierTransform(),
229
230
// ESLint fixing
231
transform.eslintTransform(),
232
233
// Custom transform
234
transform((file) => {
235
if (file.path.endsWith('.js')) {
236
file.contents = Buffer.from(
237
file.contents.toString().replace(/var /g, 'const ')
238
);
239
}
240
return file;
241
})
242
]);
243
```
244
245
## Package Manager Detection
246
247
The package manager installation system automatically detects the preferred package manager:
248
249
### Detection Priority
250
251
1. **Explicit Option**: `nodePackageManager` option
252
2. **Lock Files**: Presence of `yarn.lock`, `pnpm-lock.yaml`, etc.
253
3. **Environment Variables**: `npm_config_user_agent`
254
4. **Fallback**: npm (default)
255
256
### Supported Package Managers
257
258
- **npm**: Default Node.js package manager
259
- **yarn**: Yarn package manager (v1 and v2+)
260
- **pnpm**: Fast, disk space efficient package manager
261
- **bun**: Fast all-in-one JavaScript runtime (if detected)
262
263
**Custom Package Manager Example:**
264
265
```typescript
266
await packageManagerInstallTask({
267
memFs,
268
packageJsonLocation: "/project",
269
adapter,
270
customInstallTask: async (detectedPM, defaultTask) => {
271
if (detectedPM === "bun") {
272
// Custom bun installation
273
const { execa } = await import("execa");
274
await execa("bun", ["install"], { cwd: "/project" });
275
} else {
276
// Use default task for other package managers
277
await defaultTask();
278
}
279
}
280
});
281
```
282
283
## Advanced FileSystem Operations
284
285
### Memory FileSystem Integration
286
287
```typescript
288
import { create as createMemFs } from "mem-fs";
289
import { create as createEditor } from "mem-fs-editor";
290
291
// Create and configure memory filesystem
292
const memFs = createMemFs();
293
const editor = createEditor(memFs);
294
295
// Write files to memory filesystem
296
editor.write("/project/src/index.js", "console.log('Hello!');");
297
editor.copyTpl("/templates/component.js", "/project/src/component.js", {
298
name: "MyComponent"
299
});
300
301
// Commit to disk
302
await commitSharedFsTask({
303
adapter: myAdapter,
304
sharedFs: memFs,
305
conflicterOptions: { force: false }
306
});
307
```
308
309
### File State Management
310
311
```typescript { .api }
312
// File states in memory filesystem
313
interface MemFsEditorFile {
314
/** File path */
315
path: string;
316
/** File contents */
317
contents: Buffer | null;
318
/** File state (created, modified, deleted, etc.) */
319
state?: string;
320
/** File history */
321
history?: string[];
322
}
323
```
324
325
**File State Checking:**
326
327
```typescript
328
import { isFilePending } from "mem-fs-editor/state";
329
330
// Check if file has pending changes
331
const file = memFs.get("/project/src/index.js");
332
if (isFilePending(file)) {
333
console.log("File has pending changes");
334
}
335
```
336
337
## Error Handling
338
339
### Installation Errors
340
341
```typescript
342
try {
343
const success = await packageManagerInstallTask({
344
memFs,
345
packageJsonLocation: "/project",
346
adapter
347
});
348
349
if (!success) {
350
console.log("Installation failed but did not throw");
351
}
352
} catch (error) {
353
console.error("Installation error:", error.message);
354
355
// Handle specific error types
356
if (error.code === "ENOENT") {
357
console.error("package.json not found");
358
} else if (error.code === "EACCES") {
359
console.error("Permission denied");
360
}
361
}
362
```
363
364
### Commit Errors
365
366
```typescript
367
try {
368
await commitSharedFsTask({
369
adapter,
370
sharedFs: memFs,
371
conflicterOptions: { bail: true }
372
});
373
} catch (error) {
374
if (error.message.includes("conflict")) {
375
console.error("File conflict detected:", error.filePath);
376
} else if (error.code === "EPERM") {
377
console.error("Permission error writing file");
378
}
379
}
380
```