The Nx Devkit provides utilities for creating custom generators, executors, and plugins to extend the Nx build system for different technologies and use cases.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Package management utilities for adding dependencies, managing package.json files, and ensuring required packages are available across npm, yarn, and pnpm.
Add and remove dependencies from package.json with intelligent version resolution and conflict handling.
/**
* Add dependencies and devDependencies to package.json
* Handles version conflicts and prevents duplicate dependencies
* @param tree - File system tree
* @param dependencies - Production dependencies to add
* @param devDependencies - Development dependencies to add
* @param packageJsonPath - Path to package.json (defaults to root)
* @param keepExistingVersions - Prevent upgrading existing packages
* @returns Callback to install packages
*/
function addDependenciesToPackageJson(
tree: Tree,
dependencies: Record<string, string>,
devDependencies: Record<string, string>,
packageJsonPath?: string,
keepExistingVersions?: boolean
): GeneratorCallback;
/**
* Remove dependencies from package.json
* @param tree - File system tree
* @param dependencies - Production dependencies to remove
* @param devDependencies - Development dependencies to remove
* @param packageJsonPath - Path to package.json (defaults to root)
* @returns Callback to run package manager operations
*/
function removeDependenciesFromPackageJson(
tree: Tree,
dependencies: string[],
devDependencies: string[],
packageJsonPath?: string
): GeneratorCallback;Usage Examples:
import {
Tree,
addDependenciesToPackageJson,
removeDependenciesFromPackageJson,
GeneratorCallback
} from "@nx/devkit";
export default function myGenerator(tree: Tree): GeneratorCallback {
// Add React dependencies
const addDepsTask = addDependenciesToPackageJson(
tree,
{
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
{
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"@testing-library/react": "^13.3.0"
}
);
// Remove old dependencies
const removeDepsTask = removeDependenciesFromPackageJson(
tree,
["lodash"], // Remove from dependencies
["@types/lodash"] // Remove from devDependencies
);
// Return combined task
return async () => {
await addDepsTask();
await removeDepsTask();
};
}Manage package installation tasks and ensure required packages are available.
/**
* Create a task to install packages using the detected package manager
* @param tree - File system tree
* @param alwaysRun - Force installation even if no changes detected
* @param cwd - Working directory for installation
* @param packageManager - Specific package manager to use
*/
function installPackagesTask(
tree: Tree,
alwaysRun?: boolean,
cwd?: string,
packageManager?: PackageManager
): void;
/**
* Ensure a package is installed and available for use
* Installs the package temporarily if not found
* @param pkg - Package name to ensure
* @param version - Version to install if missing
* @returns The required package module or null for ESM packages
*/
function ensurePackage<T = any>(pkg: string, version: string): T;
/**
* Current version of Nx being used
*/
const NX_VERSION: string;Usage Examples:
import {
Tree,
installPackagesTask,
ensurePackage,
NX_VERSION
} from "@nx/devkit";
export default function myGenerator(tree: Tree) {
// Schedule package installation
installPackagesTask(tree);
// Ensure a specific package is available
try {
const chalk = ensurePackage("chalk", "^4.1.0");
console.log(chalk.green("Package is available!"));
} catch (error) {
console.error("Failed to ensure package:", error.message);
}
// Use current Nx version
console.log(`Using Nx version: ${NX_VERSION}`);
}Detect and work with different package managers (npm, yarn, pnpm).
/**
* Detect which package manager is being used in the workspace
* @param dir - Directory to check (defaults to workspace root)
* @returns Detected package manager
*/
function detectPackageManager(dir?: string): PackageManager;
/**
* Get commands for the specified package manager
* @param packageManager - Package manager to get commands for
* @returns Object with package manager commands
*/
function getPackageManagerCommand(packageManager?: PackageManager): {
install: string;
add: string;
addDev: string;
rm: string;
exec: string;
dlx: string;
list: string;
run: (script: string, args?: string) => string;
preInstall?: string;
};
/**
* Get version of the specified package manager
* @param packageManager - Package manager to check
* @returns Version string
*/
function getPackageManagerVersion(packageManager?: PackageManager): string;
/**
* Check if workspaces are enabled for the package manager
* @param packageManager - Package manager to check
* @param root - Workspace root directory
* @returns Whether workspaces are enabled
*/
function isWorkspacesEnabled(
packageManager: PackageManager,
root: string
): boolean;
/**
* Supported package managers
*/
type PackageManager = "npm" | "yarn" | "pnpm";Usage Examples:
import {
detectPackageManager,
getPackageManagerCommand,
getPackageManagerVersion,
isWorkspacesEnabled,
workspaceRoot
} from "@nx/devkit";
function setupPackageManager() {
// Detect current package manager
const pm = detectPackageManager();
console.log(`Using package manager: ${pm}`);
// Get package manager commands
const commands = getPackageManagerCommand(pm);
console.log(`Install command: ${commands.install}`);
console.log(`Add dev dependency: ${commands.addDev}`);
// Check version
const version = getPackageManagerVersion(pm);
console.log(`${pm} version: ${version}`);
// Check workspace support
const workspacesEnabled = isWorkspacesEnabled(pm, workspaceRoot);
console.log(`Workspaces enabled: ${workspacesEnabled}`);
// Run package manager commands
const { execSync } = require("child_process");
// Install dependencies
execSync(commands.install, { cwd: workspaceRoot, stdio: "inherit" });
// Add a new dependency
execSync(`${commands.add} lodash@^4.17.21`, {
cwd: workspaceRoot,
stdio: "inherit"
});
// Run a script
const testCommand = commands.run("test", "--coverage");
execSync(testCommand, { cwd: workspaceRoot, stdio: "inherit" });
}The dependency management functions use intelligent version resolution:
keepExistingVersions: true to prevent version bumps// Example of complex dependency management
export default function myGenerator(tree: Tree) {
const addDepsTask = addDependenciesToPackageJson(
tree,
{
"react": "^18.2.0", // Will upgrade from ^17.x.x
"lodash": "latest", // Uses latest tag
"moment": "~2.29.0" // Patch-level updates only
},
{
"@types/react": "^18.0.15",
"typescript": "next" // Uses next tag
},
"package.json",
false // Allow version upgrades
);
return addDepsTask;
}Working with packages in monorepo environments:
// Add dependencies to a specific project
export default function addToProject(tree: Tree, options: { project: string }) {
const projectConfig = readProjectConfiguration(tree, options.project);
const packageJsonPath = `${projectConfig.root}/package.json`;
const addDepsTask = addDependenciesToPackageJson(
tree,
{ "axios": "^1.0.0" },
{ "@types/axios": "^1.0.0" },
packageJsonPath // Project-specific package.json
);
return addDepsTask;
}// Configure package manager behavior
function configurePackageManager(tree: Tree, packageManager: PackageManager) {
const nxJson = readNxJson(tree);
updateNxJson(tree, {
...nxJson,
cli: {
...nxJson?.cli,
packageManager: packageManager
}
});
// Create package manager specific configuration files
switch (packageManager) {
case "yarn":
if (getPackageManagerVersion("yarn").startsWith("3.")) {
tree.write(".yarnrc.yml", "nodeLinker: node-modules\nenableScripts: false");
}
break;
case "pnpm":
tree.write(".npmrc", "shamefully-hoist=true");
break;
}
}// Dynamically load packages with fallback handling
async function loadPackageDynamic<T>(packageName: string, version: string): Promise<T | null> {
try {
// Try to require the package
return require(packageName);
} catch (error) {
if (error.code === 'MODULE_NOT_FOUND') {
// Package not found, ensure it's available
const pkg = ensurePackage<T>(packageName, version);
return pkg;
} else if (error.code === 'ERR_REQUIRE_ESM') {
// ESM package, use dynamic import
const pkg = await import(packageName);
return pkg.default || pkg;
}
throw error;
}
}
// Usage example
export default async function myGenerator(tree: Tree) {
// Load chalk dynamically
const chalk = await loadPackageDynamic("chalk", "^4.1.0");
if (chalk) {
console.log(chalk.green("Successfully loaded chalk!"));
}
// Load an ESM package
const prettier = await loadPackageDynamic("prettier", "^2.8.0");
if (prettier) {
const formatted = await prettier.format("const x=1;", { parser: "typescript" });
console.log("Formatted code:", formatted);
}
}Install with Tessl CLI
npx tessl i tessl/npm-nx--devkit