Handles the lifecycle and bootstrapping of generators in a specific environment
Memory filesystem operations, conflict resolution, and file transforms for generator output management and package installation tasks.
Commits the memory filesystem to disk with conflict resolution and transforms.
/**
* Commits the MemFs to disc with conflict resolution
* @param options - Configuration for commit operation
*/
function commitSharedFsTask(options: {
/** I/O adapter for user interaction */
adapter: InputOutputAdapter;
/** Options for conflict resolution */
conflicterOptions?: ConflicterOptions;
/** Memory filesystem store to commit */
sharedFs: Store<MemFsEditorFile>;
}): Promise<void>;Usage Example:
import { commitSharedFsTask } from "yeoman-environment";
import { create as createMemFs } from "mem-fs";
import { QueuedAdapter } from "@yeoman/adapter";
// Create memory filesystem and adapter
const memFs = createMemFs();
const adapter = new QueuedAdapter();
// Commit filesystem with conflict resolution
await commitSharedFsTask({
adapter,
sharedFs: memFs,
conflicterOptions: {
force: false,
bail: false,
whitespace: true
}
});Execute package manager install with auto-detection and error handling.
/**
* Executes package manager install with detection and error handling
* @param options - Configuration for package manager installation
* @returns Promise resolving to true if installation succeeded
*/
function packageManagerInstallTask(options: PackageManagerInstallTaskOptions): Promise<boolean>;
interface PackageManagerInstallTaskOptions {
/** Memory filesystem store */
memFs: Store<MemFsEditorFile>;
/** Path to directory containing package.json */
packageJsonLocation: string;
/** I/O adapter for user interaction */
adapter: InputOutputAdapter;
/** Preferred package manager (npm, yarn, pnpm) */
nodePackageManager?: string;
/** Custom install task function or boolean to disable */
customInstallTask?: boolean | ((nodePackageManager: string | undefined, defaultTask: () => Promise<boolean>) => void | Promise<void>);
/** Skip installation entirely */
skipInstall?: boolean;
}Usage Examples:
import { packageManagerInstallTask } from "yeoman-environment";
import { create as createMemFs } from "mem-fs";
import { QueuedAdapter } from "@yeoman/adapter";
const memFs = createMemFs();
const adapter = new QueuedAdapter();
// Basic package manager install
const success = await packageManagerInstallTask({
memFs,
packageJsonLocation: "/project/path",
adapter
});
// With preferred package manager
await packageManagerInstallTask({
memFs,
packageJsonLocation: "/project/path",
adapter,
nodePackageManager: "yarn"
});
// With custom install task
await packageManagerInstallTask({
memFs,
packageJsonLocation: "/project/path",
adapter,
customInstallTask: async (packageManager, defaultTask) => {
console.log(`Installing with ${packageManager}...`);
await defaultTask();
console.log("Installation complete!");
}
});
// Skip installation
await packageManagerInstallTask({
memFs,
packageJsonLocation: "/project/path",
adapter,
skipInstall: true
});File system methods available on Environment instances for generator operations.
/**
* Watch for package manager install operations
* Available on Environment and EnvironmentBase instances
* @param options - Watch configuration options
*/
watchForPackageManagerInstall(options?: any): void;
/**
* Apply transforms to filesystem
* Available on Environment and EnvironmentBase instances
* @param transformStreams - Transform streams to apply
* @param options - Transform options
*/
applyTransforms(transformStreams: any[], options?: any): Promise<void>;
/**
* Install local generator packages
* Available on Environment instances
* @param packages - Map of package names to versions
* @returns True if installation succeeded
*/
installLocalGenerators(packages: Record<string, string | undefined>): Promise<boolean>;Usage Examples:
import { createEnv } from "yeoman-environment";
const env = createEnv();
// Watch for package manager operations
env.watchForPackageManagerInstall({
detectExisting: true,
watchGlob: "**/package.json"
});
// Apply file transforms
await env.applyTransforms([
myTransformStream,
anotherTransform
], {
filter: (file) => file.path.endsWith('.js')
});
// Install local generators
const installed = await env.installLocalGenerators({
"generator-webapp": "^4.0.1",
"generator-node": "latest"
});The commit process includes sophisticated conflict resolution:
interface ConflicterOptions {
/** Override all existing files without prompting */
force?: boolean;
/** Fail immediately on first file conflict */
bail?: boolean;
/** Treat whitespace-only changes as non-conflicts */
whitespace?: boolean;
/** Skip conflict resolution for .yo-resolve files */
skipYoResolve?: boolean;
/** Custom conflict resolution adapter */
adapter?: InputOutputAdapter;
/** Dry run mode - show conflicts without applying */
dryRun?: boolean;
}Conflict Resolution Strategies:
The commit process supports transform streams for file processing:
import { transform } from "@yeoman/transform";
// Example transform usage
await env.applyTransforms([
// Prettier formatting
transform.prettierTransform(),
// ESLint fixing
transform.eslintTransform(),
// Custom transform
transform((file) => {
if (file.path.endsWith('.js')) {
file.contents = Buffer.from(
file.contents.toString().replace(/var /g, 'const ')
);
}
return file;
})
]);The package manager installation system automatically detects the preferred package manager:
nodePackageManager optionyarn.lock, pnpm-lock.yaml, etc.npm_config_user_agentCustom Package Manager Example:
await packageManagerInstallTask({
memFs,
packageJsonLocation: "/project",
adapter,
customInstallTask: async (detectedPM, defaultTask) => {
if (detectedPM === "bun") {
// Custom bun installation
const { execa } = await import("execa");
await execa("bun", ["install"], { cwd: "/project" });
} else {
// Use default task for other package managers
await defaultTask();
}
}
});import { create as createMemFs } from "mem-fs";
import { create as createEditor } from "mem-fs-editor";
// Create and configure memory filesystem
const memFs = createMemFs();
const editor = createEditor(memFs);
// Write files to memory filesystem
editor.write("/project/src/index.js", "console.log('Hello!');");
editor.copyTpl("/templates/component.js", "/project/src/component.js", {
name: "MyComponent"
});
// Commit to disk
await commitSharedFsTask({
adapter: myAdapter,
sharedFs: memFs,
conflicterOptions: { force: false }
});// File states in memory filesystem
interface MemFsEditorFile {
/** File path */
path: string;
/** File contents */
contents: Buffer | null;
/** File state (created, modified, deleted, etc.) */
state?: string;
/** File history */
history?: string[];
}File State Checking:
import { isFilePending } from "mem-fs-editor/state";
// Check if file has pending changes
const file = memFs.get("/project/src/index.js");
if (isFilePending(file)) {
console.log("File has pending changes");
}try {
const success = await packageManagerInstallTask({
memFs,
packageJsonLocation: "/project",
adapter
});
if (!success) {
console.log("Installation failed but did not throw");
}
} catch (error) {
console.error("Installation error:", error.message);
// Handle specific error types
if (error.code === "ENOENT") {
console.error("package.json not found");
} else if (error.code === "EACCES") {
console.error("Permission denied");
}
}try {
await commitSharedFsTask({
adapter,
sharedFs: memFs,
conflicterOptions: { bail: true }
});
} catch (error) {
if (error.message.includes("conflict")) {
console.error("File conflict detected:", error.filePath);
} else if (error.code === "EPERM") {
console.error("Permission error writing file");
}
}Install with Tessl CLI
npx tessl i tessl/npm-yeoman-environment