Angular workspace definition system for reading, writing, and manipulating Angular CLI workspace configurations with support for projects, targets, and extensions.
Fundamental interfaces for defining Angular workspace structure.
/**
* Root workspace definition containing projects and extensions
* Represents the complete workspace configuration
*/
interface WorkspaceDefinition {
/** Extension properties for workspace-level configuration */
readonly extensions: Record<string, JsonValue | undefined>;
/** Collection of projects in this workspace */
readonly projects: ProjectDefinitionCollection;
}
/**
* Individual project definition within a workspace
* Contains project-specific configuration, targets, and metadata
*/
interface ProjectDefinition {
/** Extension properties for project-level configuration */
readonly extensions: Record<string, JsonValue | undefined>;
/** Collection of build targets for this project */
readonly targets: TargetDefinitionCollection;
/** Root directory path for the project relative to workspace */
root: string;
/** Optional prefix for generated selectors and file names */
prefix?: string;
/** Optional source root directory relative to project root */
sourceRoot?: string;
}
/**
* Build target definition specifying how to build, test, or serve a project
* Contains builder reference, options, and configuration variants
*/
interface TargetDefinition {
/** Reference to the builder package and function */
builder: string;
/** Default options for this target */
options?: Record<string, JsonValue | undefined>;
/** Named configuration variants with option overrides */
configurations?: Record<string, Record<string, JsonValue | undefined> | undefined>;
}
/**
* Listener function type for collection change events
* Called when items are added, removed, or modified in collections
*/
type DefinitionCollectionListener<T> = (
name: string,
newValue: T | undefined,
collection: DefinitionCollection<T>
) => void;Map-like collections for managing projects and targets with change tracking.
/**
* Base interface for definition collections
* Provides Map-like interface with additional workspace-specific functionality
*/
interface DefinitionCollection<T> extends ReadonlyMap<string, T> {
/**
* Add a new item to the collection
* @param name - Item name/key
* @param definition - Item definition
* @returns The added item
*/
add(name: string, definition: T): T;
/**
* Set or update an item in the collection
* @param name - Item name/key
* @param definition - Item definition
* @returns The collection instance for chaining
*/
set(name: string, definition: T): this;
/**
* Remove an item from the collection
* @param name - Item name/key to remove
* @returns True if item was removed, false if not found
*/
delete(name: string): boolean;
/**
* Add a listener for collection changes
* @param listener - Function to call on changes
*/
addListener(listener: DefinitionCollectionListener<T>): void;
/**
* Remove a change listener
* @param listener - Listener function to remove
*/
removeListener(listener: DefinitionCollectionListener<T>): void;
}
/**
* Map-like collection for managing projects within a workspace
* Provides change tracking and validation for project definitions
*/
class ProjectDefinitionCollection implements DefinitionCollection<ProjectDefinition> {
/** Number of projects in the collection */
readonly size: number;
/**
* Get a project by name
* @param name - Project name
* @returns Project definition or undefined if not found
*/
get(name: string): ProjectDefinition | undefined;
/**
* Check if a project exists
* @param name - Project name
* @returns True if project exists
*/
has(name: string): boolean;
/**
* Add a new project to the workspace
* @param name - Project name (must be unique)
* @param definition - Project definition
* @returns The added project definition
*/
add(name: string, definition: ProjectDefinition): ProjectDefinition;
/**
* Set or update a project
* @param name - Project name
* @param definition - Project definition
* @returns The collection instance for chaining
*/
set(name: string, definition: ProjectDefinition): this;
/**
* Remove a project from the workspace
* @param name - Project name to remove
* @returns True if project was removed
*/
delete(name: string): boolean;
/**
* Iterate over project names
*/
keys(): IterableIterator<string>;
/**
* Iterate over project definitions
*/
values(): IterableIterator<ProjectDefinition>;
/**
* Iterate over [name, definition] pairs
*/
entries(): IterableIterator<[string, ProjectDefinition]>;
/**
* Iterate over [name, definition] pairs
*/
[Symbol.iterator](): IterableIterator<[string, ProjectDefinition]>;
addListener(listener: DefinitionCollectionListener<ProjectDefinition>): void;
removeListener(listener: DefinitionCollectionListener<ProjectDefinition>): void;
}
/**
* Map-like collection for managing build targets within a project
* Provides change tracking and validation for target definitions
*/
class TargetDefinitionCollection implements DefinitionCollection<TargetDefinition> {
/** Number of targets in the collection */
readonly size: number;
/**
* Get a target by name
* @param name - Target name
* @returns Target definition or undefined if not found
*/
get(name: string): TargetDefinition | undefined;
/**
* Check if a target exists
* @param name - Target name
* @returns True if target exists
*/
has(name: string): boolean;
/**
* Add a new target to the project
* @param name - Target name (must be unique within project)
* @param definition - Target definition
* @returns The added target definition
*/
add(name: string, definition: TargetDefinition): TargetDefinition;
/**
* Set or update a target
* @param name - Target name
* @param definition - Target definition
* @returns The collection instance for chaining
*/
set(name: string, definition: TargetDefinition): this;
/**
* Remove a target from the project
* @param name - Target name to remove
* @returns True if target was removed
*/
delete(name: string): boolean;
/**
* Iterate over target names
*/
keys(): IterableIterator<string>;
/**
* Iterate over target definitions
*/
values(): IterableIterator<TargetDefinition>;
/**
* Iterate over [name, definition] pairs
*/
entries(): IterableIterator<[string, TargetDefinition]>;
/**
* Iterate over [name, definition] pairs
*/
[Symbol.iterator](): IterableIterator<[string, TargetDefinition]>;
addListener(listener: DefinitionCollectionListener<TargetDefinition>): void;
removeListener(listener: DefinitionCollectionListener<TargetDefinition>): void;
}Core functions for reading and writing workspace configurations.
/**
* Host interface for workspace file operations
* Abstracts file system operations for workspace management
*/
interface WorkspaceHost {
/**
* Read file content as string
* @param path - File path to read
* @returns Promise resolving to file content
*/
readFile(path: string): Promise<string>;
/**
* Write file content
* @param path - File path to write
* @param data - Content to write
* @returns Promise completing when write is done
*/
writeFile(path: string, data: string): Promise<void>;
/**
* Check if path is a directory
* @param path - Path to check
* @returns Promise resolving to true if path is directory
*/
isDirectory(path: string): Promise<boolean>;
/**
* Check if path is a file
* @param path - Path to check
* @returns Promise resolving to true if path is file
*/
isFile(path: string): Promise<boolean>;
}
/**
* Create a workspace host from a virtual file system host
* Adapts virtual FS host interface to workspace host interface
* @param host - Virtual file system host
* @returns Workspace host implementation
*/
function createWorkspaceHost(host: virtualFs.Host): WorkspaceHost;
/**
* Supported workspace file formats
*/
enum WorkspaceFormat {
/** JSON format used by Angular CLI */
JSON = 0
}
/**
* Read workspace definition from host
* Parses workspace configuration and creates definition objects
* @param path - Path to workspace directory or configuration file
* @param host - Host for file operations
* @param format - Optional format specification (defaults to JSON)
* @returns Promise resolving to workspace definition and metadata
*/
function readWorkspace(
path: string,
host: WorkspaceHost,
format?: WorkspaceFormat
): Promise<{ workspace: WorkspaceDefinition }>;
/**
* Write workspace definition to host
* Serializes workspace definition and writes to storage
* @param workspace - Workspace definition to write
* @param host - Host for file operations
* @param path - Optional path override (uses original path if not specified)
* @param format - Optional format specification (defaults to JSON)
* @returns Promise completing when write is done
*/
function writeWorkspace(
workspace: WorkspaceDefinition,
host: WorkspaceHost,
path?: string,
format?: WorkspaceFormat
): Promise<void>;Additional utilities for workspace management and metadata handling.
/**
* Workspace metadata containing format and location information
*/
interface WorkspaceMetadata {
/** Format of the workspace file */
format: WorkspaceFormat;
/** Path to the workspace file */
filePath: string;
/** Whether the workspace uses the new format */
isNewFormat: boolean;
}
/**
* Options for workspace reading operations
*/
interface ReadWorkspaceOptions {
/** Whether to allow legacy format */
allowLegacy?: boolean;
/** Custom JSON schema for validation */
schema?: JsonObject;
}
/**
* Options for workspace writing operations
*/
interface WriteWorkspaceOptions {
/** Whether to format the output JSON */
pretty?: boolean;
/** Custom indentation for JSON formatting */
indent?: string | number;
}
/**
* Validate workspace definition against schema
* @param workspace - Workspace definition to validate
* @param schema - Optional schema for validation
* @returns Validation result with errors if any
*/
function validateWorkspace(
workspace: WorkspaceDefinition,
schema?: JsonObject
): { valid: boolean; errors: string[] };
/**
* Create a new empty workspace definition
* @returns New workspace definition with empty projects collection
*/
function createWorkspaceDefinition(): WorkspaceDefinition;
/**
* Create a new empty project definition
* @param root - Project root directory
* @param options - Optional project configuration
* @returns New project definition
*/
function createProjectDefinition(
root: string,
options?: {
prefix?: string;
sourceRoot?: string;
extensions?: Record<string, JsonValue>;
}
): ProjectDefinition;
/**
* Create a new target definition
* @param builder - Builder package and function reference
* @param options - Optional target configuration
* @returns New target definition
*/
function createTargetDefinition(
builder: string,
options?: {
options?: Record<string, JsonValue>;
configurations?: Record<string, Record<string, JsonValue>>;
}
): TargetDefinition;import { workspaces, virtualFs } from "@angular-devkit/core";
import { NodeJsSyncHost } from "@angular-devkit/core/node";
// Create workspace host
const fsHost = new NodeJsSyncHost();
const host = workspaces.createWorkspaceHost(fsHost);
// Read workspace
const { workspace } = await workspaces.readWorkspace('/path/to/workspace/', host);
console.log(`Workspace has ${workspace.projects.size} projects`);
// List all projects
for (const [name, project] of workspace.projects) {
console.log(`Project: ${name} at ${project.root}`);
console.log(` Targets: ${Array.from(project.targets.keys()).join(', ')}`);
}
// Modify workspace
const newProject = workspaces.createProjectDefinition('projects/new-app', {
prefix: 'app',
sourceRoot: 'projects/new-app/src'
});
workspace.projects.add('new-app', newProject);
// Write workspace back
await workspaces.writeWorkspace(workspace, host);import { workspaces } from "@angular-devkit/core";
// Get specific project
const project = workspace.projects.get('my-app');
if (!project) {
throw new Error('Project not found');
}
// Add a new target
const buildTarget = workspaces.createTargetDefinition(
'@angular-devkit/build-angular:browser',
{
options: {
outputPath: 'dist/my-app',
index: 'src/index.html',
main: 'src/main.ts',
polyfills: 'src/polyfills.ts',
tsConfig: 'tsconfig.app.json'
},
configurations: {
production: {
budgets: [{
type: 'initial',
maximumWarning: '2mb',
maximumError: '5mb'
}],
outputHashing: 'all'
},
development: {
buildOptimizer: false,
optimization: false,
vendorChunk: true,
extractLicenses: false,
sourceMap: true,
namedChunks: true
}
}
}
);
project.targets.add('build', buildTarget);
// Modify existing target
const testTarget = project.targets.get('test');
if (testTarget && testTarget.options) {
testTarget.options.codeCoverage = true;
testTarget.options.browsers = 'ChromeHeadless';
}import { workspaces } from "@angular-devkit/core";
// Add workspace-level extensions
workspace.extensions['version'] = '1.0.0';
workspace.extensions['cli'] = {
defaultCollection: '@angular/schematics',
analytics: false
};
// Add project-level extensions
const project = workspace.projects.get('my-app')!;
project.extensions['architect'] = {
defaultProject: 'my-app'
};
// Access target configurations
const buildTarget = project.targets.get('build')!;
console.log('Available configurations:', Object.keys(buildTarget.configurations || {}));
// Get specific configuration
const prodConfig = buildTarget.configurations?.['production'];
if (prodConfig) {
console.log('Production output hashing:', prodConfig.outputHashing);
}import { workspaces } from "@angular-devkit/core";
// Add listeners for project changes
workspace.projects.addListener((name, newValue, collection) => {
if (newValue) {
console.log(`Project ${name} was added or updated`);
} else {
console.log(`Project ${name} was removed`);
}
});
// Add listeners for target changes in a specific project
const project = workspace.projects.get('my-app')!;
project.targets.addListener((name, newValue, collection) => {
if (newValue) {
console.log(`Target ${name} was added or updated in my-app`);
console.log(`Builder: ${newValue.builder}`);
} else {
console.log(`Target ${name} was removed from my-app`);
}
});
// Make changes (listeners will be called)
project.targets.add('deploy', {
builder: '@angular/fire:deploy',
options: {
prerender: true
}
});import { workspaces } from "@angular-devkit/core";
// Validate workspace
const validationResult = workspaces.validateWorkspace(workspace);
if (!validationResult.valid) {
console.error('Workspace validation failed:');
validationResult.errors.forEach(error => {
console.error(` - ${error}`);
});
} else {
console.log('Workspace is valid');
}
// Create workspace from scratch
const newWorkspace = workspaces.createWorkspaceDefinition();
// Add workspace-level configuration
newWorkspace.extensions['version'] = 1;
newWorkspace.extensions['newProjectRoot'] = 'projects';
// Add a library project
const libProject = workspaces.createProjectDefinition('projects/my-lib', {
prefix: 'lib',
sourceRoot: 'projects/my-lib/src'
});
// Add build target for library
const libBuildTarget = workspaces.createTargetDefinition(
'@angular-devkit/build-angular:ng-packagr',
{
options: {
project: 'projects/my-lib/ng-package.json'
}
}
);
libProject.targets.add('build', libBuildTarget);
newWorkspace.projects.add('my-lib', libProject);
// Write new workspace
await workspaces.writeWorkspace(newWorkspace, host, '/path/to/new-workspace/');