CLI compatibility layer for older Nx workspaces that serves as a bridge to core nx functionality
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Utilities for finding and working with workspace root directories. Provides reliable detection of Nx workspace boundaries and root path resolution.
Pre-calculated workspace root directory for the current process.
/**
* The root directory of the current workspace
* Automatically detected by looking for nx.json, workspace.json, or nx executable
*/
export const workspaceRoot: string;Usage Examples:
import { workspaceRoot } from "@nrwl/tao/utils/app-root";
console.log('Current workspace root:', workspaceRoot);
// Use in file operations
const nxJsonPath = `${workspaceRoot}/nx.json`;
const packageJsonPath = `${workspaceRoot}/package.json`;
// Use with other APIs
import { Workspaces } from "@nrwl/tao/shared/workspace";
const workspaces = new Workspaces(workspaceRoot);Internal function for finding workspace root directories.
/**
* Internal function to find workspace root by looking for nx.json, nx, or nx.bat files
* @param dir - Directory to start searching from
* @param candidateRoot - Previously found candidate root (for recursion)
* @returns Path to the workspace root directory
*/
export function workspaceRootInner(dir: string, candidateRoot: string | null): string;Usage Examples:
import { workspaceRootInner } from "@nrwl/tao/utils/app-root";
// Find workspace root starting from specific directory
const rootFromPath = workspaceRootInner('/path/to/some/nested/directory', null);
console.log('Workspace root:', rootFromPath);
// Find workspace root with candidate (useful for optimization)
const rootWithCandidate = workspaceRootInner(
'/path/to/nested/dir',
'/path/to/potential/root'
);The workspace root detection follows this priority order:
import { workspaceRoot } from "@nrwl/tao/utils/app-root";
import { existsSync, readFileSync } from 'fs';
import { join } from 'path';
// Check if we're in an Nx workspace
function isNxWorkspace(): boolean {
return existsSync(join(workspaceRoot, 'nx.json'));
}
// Read workspace package.json
function getWorkspacePackageJson() {
const packageJsonPath = join(workspaceRoot, 'package.json');
if (existsSync(packageJsonPath)) {
return JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
}
return null;
}
// Get relative path from workspace root
function getRelativePath(absolutePath: string): string {
return absolutePath.replace(workspaceRoot, '').replace(/^\//, '');
}
console.log('Is Nx workspace:', isNxWorkspace());
console.log('Workspace package.json:', getWorkspacePackageJson());
console.log('Current relative path:', getRelativePath(process.cwd()));import { workspaceRoot } from "@nrwl/tao/utils/app-root";
import { join, relative, resolve } from 'path';
class WorkspacePathUtils {
static resolveFromRoot(...paths: string[]): string {
return join(workspaceRoot, ...paths);
}
static resolveToRoot(path: string): string {
return resolve(workspaceRoot, path);
}
static getRelativeToRoot(absolutePath: string): string {
return relative(workspaceRoot, absolutePath);
}
static isInWorkspace(path: string): boolean {
const relativePath = this.getRelativeToRoot(path);
return !relativePath.startsWith('../');
}
}
// Usage examples
const appPath = WorkspacePathUtils.resolveFromRoot('apps', 'my-app');
const libPath = WorkspacePathUtils.resolveFromRoot('libs', 'shared', 'ui');
const configPath = WorkspacePathUtils.resolveFromRoot('nx.json');
console.log('App path:', appPath);
console.log('Lib path:', libPath);
console.log('Config path:', configPath);
// Check if paths are within workspace
console.log('Is app in workspace:', WorkspacePathUtils.isInWorkspace(appPath));
console.log('Is external path in workspace:', WorkspacePathUtils.isInWorkspace('/tmp/external'));import { workspaceRootInner } from "@nrwl/tao/utils/app-root";
import { existsSync } from 'fs';
import { dirname } from 'path';
function findWorkspaceRoot(startPath: string): string | null {
try {
return workspaceRootInner(startPath, null);
} catch (error) {
return null;
}
}
function findNearestWorkspace(filePath: string): string | null {
let currentDir = dirname(filePath);
// Search up the directory tree
while (currentDir !== dirname(currentDir)) {
const workspaceRoot = findWorkspaceRoot(currentDir);
if (workspaceRoot) {
return workspaceRoot;
}
currentDir = dirname(currentDir);
}
return null;
}
// Usage
const fileInProject = '/path/to/deep/nested/file.ts';
const nearestWorkspace = findNearestWorkspace(fileInProject);
if (nearestWorkspace) {
console.log(`Found workspace: ${nearestWorkspace}`);
} else {
console.log('No workspace found');
}import { workspaceRootInner } from "@nrwl/tao/utils/app-root";
import { readdirSync, statSync } from 'fs';
import { join } from 'path';
class MultiWorkspaceManager {
private workspaces: Map<string, string> = new Map();
constructor(private searchRoot: string) {
this.discoverWorkspaces();
}
private discoverWorkspaces(): void {
this.searchDirectory(this.searchRoot);
}
private searchDirectory(dir: string): void {
try {
const entries = readdirSync(dir);
for (const entry of entries) {
const fullPath = join(dir, entry);
if (statSync(fullPath).isDirectory()) {
// Try to find workspace in this directory
try {
const workspaceRoot = workspaceRootInner(fullPath, null);
if (workspaceRoot && !this.workspaces.has(workspaceRoot)) {
this.workspaces.set(entry, workspaceRoot);
}
} catch {
// Not a workspace, continue searching subdirectories
this.searchDirectory(fullPath);
}
}
}
} catch (error) {
// Directory might not be accessible
}
}
getWorkspaces(): Map<string, string> {
return new Map(this.workspaces);
}
getWorkspaceByName(name: string): string | undefined {
return this.workspaces.get(name);
}
getAllWorkspaceRoots(): string[] {
return Array.from(this.workspaces.values());
}
}
// Usage
const manager = new MultiWorkspaceManager('/path/to/monorepo');
const workspaces = manager.getWorkspaces();
console.log('Discovered workspaces:');
workspaces.forEach((root, name) => {
console.log(` ${name}: ${root}`);
});import { workspaceRoot } from "@nrwl/tao/utils/app-root";
function setupWorkspaceEnvironment() {
// Set environment variables for tools
process.env.NX_WORKSPACE_ROOT = workspaceRoot;
process.env.WORKSPACE_ROOT = workspaceRoot;
// Add workspace bin to PATH
const workspaceBin = `${workspaceRoot}/node_modules/.bin`;
if (!process.env.PATH?.includes(workspaceBin)) {
process.env.PATH = `${workspaceBin}:${process.env.PATH}`;
}
// Set working directory to workspace root if needed
if (process.cwd() !== workspaceRoot) {
console.log(`Changing directory to workspace root: ${workspaceRoot}`);
process.chdir(workspaceRoot);
}
}
// Validate workspace environment
function validateWorkspaceEnvironment(): boolean {
const checks = [
{ name: 'Workspace root exists', check: () => existsSync(workspaceRoot) },
{ name: 'nx.json exists', check: () => existsSync(`${workspaceRoot}/nx.json`) },
{ name: 'package.json exists', check: () => existsSync(`${workspaceRoot}/package.json`) },
{ name: 'node_modules exists', check: () => existsSync(`${workspaceRoot}/node_modules`) }
];
let allValid = true;
checks.forEach(({ name, check }) => {
const isValid = check();
console.log(`${isValid ? '✓' : '✗'} ${name}`);
if (!isValid) allValid = false;
});
return allValid;
}
// Usage
setupWorkspaceEnvironment();
const isValid = validateWorkspaceEnvironment();
if (!isValid) {
console.error('Workspace environment validation failed');
process.exit(1);
}The workspace root utilities integrate with all other @nrwl/tao APIs:
import { workspaceRoot } from "@nrwl/tao/utils/app-root";
import { Workspaces } from "@nrwl/tao/shared/workspace";
import { detectPackageManager } from "@nrwl/tao/shared/package-manager";
import { FsTree } from "@nrwl/tao/shared/tree";
import { logger } from "@nrwl/tao/shared/logger";
function initializeWorkspaceTools() {
logger.info(`Initializing tools for workspace: ${workspaceRoot}`);
// Initialize workspace management
const workspaces = new Workspaces(workspaceRoot);
const hasNxJson = workspaces.hasNxJson();
if (!hasNxJson) {
logger.error('Not a valid Nx workspace');
return null;
}
// Initialize package manager detection
const packageManager = detectPackageManager(workspaceRoot);
logger.info(`Detected package manager: ${packageManager}`);
// Initialize virtual file system
const tree = new FsTree(workspaceRoot);
// Read workspace configuration
const nxJson = workspaces.readNxJson();
const projects = workspaces.readProjectsConfigurations();
logger.info(`Workspace '${nxJson.npmScope || 'unnamed'}' initialized`);
logger.info(`Found ${Object.keys(projects.projects).length} projects`);
return {
root: workspaceRoot,
workspaces,
packageManager,
tree,
nxJson,
projects
};
}
// Usage
const tools = initializeWorkspaceTools();
if (tools) {
console.log('Workspace tools initialized successfully');
console.log(`Working in: ${tools.root}`);
}Install with Tessl CLI
npx tessl i tessl/npm-nrwl--tao