Node.js specific implementations including file system hosts and console logging capabilities, plus testing utilities for creating temporary file systems in test environments.
File system host implementations that interface with the Node.js fs module.
/**
* Asynchronous Node.js file system host implementation
* Provides virtual file system interface backed by Node.js fs module
* All operations are asynchronous and return Observables
*/
class NodeJsAsyncHost implements virtualFs.Host<fs.Stats> {
/** Host capabilities indicating support for synchronous operations */
readonly capabilities: HostCapabilities;
constructor();
/**
* Read file content as buffer
* @param path - Normalized path to file
* @returns Observable emitting file content buffer
*/
read(path: Path): Observable<FileBuffer>;
/**
* Write content to file, creating directories as needed
* @param path - Normalized path for new file
* @param content - Buffer content to write
* @returns Observable completing when write is done
*/
write(path: Path, content: FileBufferLike): Observable<void>;
/**
* Delete file or directory recursively
* @param path - Normalized path to delete
* @returns Observable completing when deletion is done
*/
delete(path: Path): Observable<void>;
/**
* Rename/move file or directory
* @param from - Source path
* @param to - Destination path
* @returns Observable completing when rename is done
*/
rename(from: Path, to: Path): Observable<void>;
/**
* List directory contents
* @param path - Normalized directory path
* @returns Observable emitting array of path fragments
*/
list(path: Path): Observable<PathFragment[]>;
/**
* Check if path exists
* @param path - Normalized path to check
* @returns Observable emitting boolean result
*/
exists(path: Path): Observable<boolean>;
/**
* Check if path is a directory
* @param path - Normalized path to check
* @returns Observable emitting boolean result
*/
isDirectory(path: Path): Observable<boolean>;
/**
* Check if path is a file
* @param path - Normalized path to check
* @returns Observable emitting boolean result
*/
isFile(path: Path): Observable<boolean>;
/**
* Get file/directory statistics
* @param path - Normalized path to stat
* @returns Observable emitting Node.js fs.Stats object
*/
stat(path: Path): Observable<Stats<fs.Stats>>;
/**
* Watch path for changes using Node.js fs.watch
* @param path - Normalized path to watch
* @param options - Watch configuration options
* @returns Observable emitting watch events
*/
watch(path: Path, options?: HostWatchOptions): Observable<HostWatchEvent>;
}
/**
* Synchronous Node.js file system host implementation
* Provides virtual file system interface with synchronous operations
* Operations block until completion
*/
class NodeJsSyncHost implements virtualFs.Host<fs.Stats> {
/** Host capabilities indicating support for synchronous operations */
readonly capabilities: HostCapabilities;
constructor();
/**
* Read file content as buffer synchronously
* @param path - Normalized path to file
* @returns Observable emitting file content buffer
*/
read(path: Path): Observable<FileBuffer>;
/**
* Write content to file synchronously, creating directories as needed
* @param path - Normalized path for new file
* @param content - Buffer content to write
* @returns Observable completing when write is done
*/
write(path: Path, content: FileBufferLike): Observable<void>;
/**
* Delete file or directory recursively and synchronously
* @param path - Normalized path to delete
* @returns Observable completing when deletion is done
*/
delete(path: Path): Observable<void>;
/**
* Rename/move file or directory synchronously
* @param from - Source path
* @param to - Destination path
* @returns Observable completing when rename is done
*/
rename(from: Path, to: Path): Observable<void>;
/**
* List directory contents synchronously
* @param path - Normalized directory path
* @returns Observable emitting array of path fragments
*/
list(path: Path): Observable<PathFragment[]>;
/**
* Check if path exists synchronously
* @param path - Normalized path to check
* @returns Observable emitting boolean result
*/
exists(path: Path): Observable<boolean>;
/**
* Check if path is a directory synchronously
* @param path - Normalized path to check
* @returns Observable emitting boolean result
*/
isDirectory(path: Path): Observable<boolean>;
/**
* Check if path is a file synchronously
* @param path - Normalized path to check
* @returns Observable emitting boolean result
*/
isFile(path: Path): Observable<boolean>;
/**
* Get file/directory statistics synchronously
* @param path - Normalized path to stat
* @returns Observable emitting Node.js fs.Stats object
*/
stat(path: Path): Observable<Stats<fs.Stats>>;
/**
* Watch path for changes using chokidar when available
* @param path - Normalized path to watch
* @param options - Watch configuration options
* @returns Observable emitting watch events
*/
watch(path: Path, options?: HostWatchOptions): Observable<HostWatchEvent>;
}Console logger implementation with color support and process output handling.
/**
* Interface for process stdout/stderr streams
* Abstracts console output for testing and customization
*/
interface ProcessOutput {
/** Write function for output stream */
write(buffer: string | Uint8Array): boolean;
}
/**
* Create console logger with color support and process output
* Provides formatted console output with level-based coloring
* @param verbose - Whether to show debug messages
* @param stdout - Optional custom stdout stream (defaults to process.stdout)
* @param stderr - Optional custom stderr stream (defaults to process.stderr)
* @returns Logger instance configured for console output
*/
function createConsoleLogger(
verbose?: boolean,
stdout?: ProcessOutput,
stderr?: ProcessOutput
): logging.Logger;Testing utilities for creating temporary file systems in test environments.
/**
* Temporary scoped synchronous host for testing
* Creates a temporary directory and scopes all operations to it
* Automatically manages cleanup of temporary files
*/
class TempScopedNodeJsSyncHost extends virtualFs.ScopedHost<fs.Stats> {
/** Synchronous delegate host for blocking operations */
protected _sync?: virtualFs.SyncDelegateHost<fs.Stats>;
/** Root path of the temporary directory */
protected override _root: Path;
/**
* Create a new temporary scoped host
* Automatically creates a unique temporary directory
*/
constructor();
/**
* Get all files in the temporary directory recursively
* @returns Array of all file paths in the temporary directory
*/
get files(): Path[];
/**
* Get the root path of the temporary directory
* @returns Normalized path to temporary directory root
*/
get root(): Path;
/**
* Get synchronous delegate host for blocking operations
* @returns Synchronous host interface
*/
get sync(): virtualFs.SyncDelegateHost<fs.Stats>;
// Inherits all Host interface methods from ScopedHost
read(path: Path): Observable<FileBuffer>;
write(path: Path, content: FileBufferLike): Observable<void>;
delete(path: Path): Observable<void>;
rename(from: Path, to: Path): Observable<void>;
list(path: Path): Observable<PathFragment[]>;
exists(path: Path): Observable<boolean>;
isDirectory(path: Path): Observable<boolean>;
isFile(path: Path): Observable<boolean>;
stat(path: Path): Observable<Stats<fs.Stats>>;
watch(path: Path, options?: HostWatchOptions): Observable<HostWatchEvent>;
}import { NodeJsSyncHost } from "@angular-devkit/core/node";
import { normalize } from "@angular-devkit/core";
const host = new NodeJsSyncHost();
const filePath = normalize('/path/to/file.txt');
// Write file
const content = new TextEncoder().encode('Hello, World!');
await host.write(filePath, content.buffer).toPromise();
// Read file
const buffer = await host.read(filePath).toPromise();
const text = new TextDecoder().decode(buffer);
console.log(text); // "Hello, World!"
// Check file existence
const exists = await host.exists(filePath).toPromise();
console.log(`File exists: ${exists}`);
// Get file stats
const stats = await host.stat(filePath).toPromise();
console.log(`File size: ${stats.content.size} bytes`);
console.log(`Modified: ${stats.content.mtime}`);import { NodeJsSyncHost } from "@angular-devkit/core/node";
import { normalize, join, fragment } from "@angular-devkit/core";
const host = new NodeJsSyncHost();
const dirPath = normalize('/my/project');
// Create directory structure by writing files
await host.write(
join(dirPath, fragment('src'), fragment('app.ts')),
new TextEncoder().encode('export class App {}').buffer
).toPromise();
await host.write(
join(dirPath, fragment('src'), fragment('main.ts')),
new TextEncoder().encode('import { App } from "./app";').buffer
).toPromise();
// List directory contents
const files = await host.list(join(dirPath, fragment('src'))).toPromise();
console.log('Files:', files); // ['app.ts', 'main.ts']
// Check if path is directory
const isDir = await host.isDirectory(dirPath).toPromise();
console.log(`${dirPath} is directory: ${isDir}`);import { NodeJsAsyncHost } from "@angular-devkit/core/node";
import { normalize, virtualFs } from "@angular-devkit/core";
const host = new NodeJsAsyncHost();
const watchPath = normalize('/my/project/src');
// Watch for file changes
const subscription = host.watch(watchPath, {
recursive: true,
exclude: ['node_modules/**', '*.log']
}).subscribe(event => {
switch (event.type) {
case virtualFs.HostWatchEventType.Created:
console.log(`File created: ${event.path}`);
break;
case virtualFs.HostWatchEventType.Changed:
console.log(`File modified: ${event.path}`);
break;
case virtualFs.HostWatchEventType.Deleted:
console.log(`File deleted: ${event.path}`);
break;
case virtualFs.HostWatchEventType.Renamed:
console.log(`File renamed: ${event.path} -> ${event.to}`);
break;
}
});
// Make some changes to trigger events
setTimeout(async () => {
await host.write(
join(watchPath, fragment('new-file.ts')),
new TextEncoder().encode('// New file').buffer
).toPromise();
}, 1000);
// Clean up watching after 10 seconds
setTimeout(() => {
subscription.unsubscribe();
}, 10000);import { createConsoleLogger } from "@angular-devkit/core/node";
// Create console logger
const logger = createConsoleLogger(true); // verbose = true
// Log messages (will appear in console with colors)
logger.debug('Debug information', { timestamp: Date.now() });
logger.info('Application started');
logger.warn('Configuration warning');
logger.error('An error occurred', { code: 'E001' });
// Create child logger
const httpLogger = logger.createChild('http');
httpLogger.info('Server listening on port 3000');
// Custom output streams
const customLogger = createConsoleLogger(
false, // not verbose
process.stdout, // custom stdout
process.stderr // custom stderr
);import { TempScopedNodeJsSyncHost } from "@angular-devkit/core/node/testing";
import { normalize, join, fragment } from "@angular-devkit/core";
// Create temporary scoped host for testing
const tempHost = new TempScopedNodeJsSyncHost();
console.log(`Temporary directory: ${tempHost.root}`);
// Write test files
await tempHost.write(
normalize('/test.txt'),
new TextEncoder().encode('Test content').buffer
).toPromise();
await tempHost.write(
normalize('/src/app.ts'),
new TextEncoder().encode('export class App {}').buffer
).toPromise();
// List all files in temp directory
console.log('All files:', tempHost.files);
// Output: ['/test.txt', '/src/app.ts']
// Use synchronous operations
const syncHost = tempHost.sync;
const content = syncHost.read(normalize('/test.txt'));
const text = new TextDecoder().decode(content);
console.log('File content:', text); // "Test content"
// Check file existence synchronously
const exists = syncHost.exists(normalize('/src/app.ts'));
console.log('App file exists:', exists); // true
// Temporary directory is automatically cleaned up when host is garbage collectedimport { workspaces } from "@angular-devkit/core";
import { NodeJsSyncHost } from "@angular-devkit/core/node";
// Use Node.js host with workspace management
const fsHost = new NodeJsSyncHost();
const workspaceHost = workspaces.createWorkspaceHost(fsHost);
// Read workspace from file system
const { workspace } = await workspaces.readWorkspace(
'/path/to/angular/workspace/',
workspaceHost
);
console.log(`Found ${workspace.projects.size} projects`);
// Modify and write back to file system
const project = workspace.projects.get('my-app');
if (project) {
project.sourceRoot = 'src';
await workspaces.writeWorkspace(workspace, workspaceHost);
console.log('Workspace updated');
}import { NodeJsSyncHost } from "@angular-devkit/core/node";
import { normalize, FileDoesNotExistException } from "@angular-devkit/core";
const host = new NodeJsSyncHost();
try {
// Try to read non-existent file
await host.read(normalize('/nonexistent/file.txt')).toPromise();
} catch (error) {
if (error instanceof FileDoesNotExistException) {
console.log('File not found:', error.message);
} else {
console.error('Unexpected error:', error);
}
}
// Check existence before reading
const filePath = normalize('/uncertain/file.txt');
const exists = await host.exists(filePath).toPromise();
if (exists) {
const content = await host.read(filePath).toPromise();
console.log('File content loaded');
} else {
console.log('File does not exist, creating...');
await host.write(filePath, new TextEncoder().encode('New file').buffer).toPromise();
}