Lerna project configuration management for monorepos with package discovery and workspace handling
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Lerna project configuration management for monorepos, providing package discovery, workspace handling, and configuration resolution for Lerna-managed multi-package repositories.
npm install @lerna/projectconst { Project } = require("@lerna/project");
const { getPackages, getPackagesSync } = require("@lerna/project");For ES modules:
import { Project, getPackages, getPackagesSync } from "@lerna/project";const { Project } = require("@lerna/project");
// Create a project instance from current working directory
const project = new Project();
// Get packages asynchronously
const packages = await project.getPackages();
console.log(packages.length); // Number of packages found
// Access project configuration
console.log(project.config.version); // Project version
console.log(project.rootPath); // Project root directory
// Static methods for convenience
const packages2 = await Project.getPackages("/path/to/monorepo");
const packagesSync = Project.getPackagesSync("/path/to/monorepo");The @lerna/project package is built around several key components:
Core class representing a Lerna project and its configuration, providing access to packages, configuration, and project metadata.
/**
* A representation of the entire project managed by Lerna
* @param {string} [cwd] - Working directory (defaults to process.cwd())
*/
class Project {
constructor(cwd?: string);
// Static convenience methods
static getPackages(cwd?: string): Promise<Package[]>;
static getPackagesSync(cwd?: string): Package[];
// Instance properties
config: ProjectConfig;
configNotFound: boolean;
rootConfigLocation: string;
rootPath: string;
version: string;
packageConfigs: string[];
packageParentDirs: string[];
manifest: Package;
pnpmWorkspaceConfig: PnpmWorkspaceConfig;
licensePath: string;
fileFinder: FileFinder;
// Instance methods
getPackages(): Promise<Package[]>;
getPackagesSync(): Package[];
getPackageLicensePaths(): Promise<string[]>;
isIndependent(): boolean;
serializeConfig(): Promise<string>;
// Static constants
static PACKAGE_GLOB: string; // "packages/*"
static LICENSE_GLOB: string; // "LICEN{S,C}E{,.*}"
}Convenience functions providing direct access to package discovery without instantiating the Project class.
/**
* Get packages for a project directory
* @param {string} [cwd] - Working directory (defaults to process.cwd())
* @returns {Promise<Package[]>} Array of Package instances
*/
function getPackages(cwd?: string): Promise<Package[]>;
/**
* Synchronously get packages for a project directory
* @param {string} [cwd] - Working directory (defaults to process.cwd())
* @returns {Package[]} Array of Package instances
*/
function getPackagesSync(cwd?: string): Package[];Methods for discovering and enumerating packages within the monorepo.
/**
* Asynchronously discover packages in the project
* @returns {Promise<Package[]>} Array of Package instances
*/
Project.prototype.getPackages(): Promise<Package[]>;
/**
* Synchronously discover packages in the project
* @returns {Package[]} Array of Package instances
*/
Project.prototype.getPackagesSync(): Package[];Usage Examples:
const { Project } = require("@lerna/project");
// Async package discovery
const project = new Project();
const packages = await project.getPackages();
packages.forEach(pkg => {
console.log(`${pkg.name}@${pkg.version} at ${pkg.location}`);
});
// Sync package discovery
const packagesSync = project.getPackagesSync();
console.log(`Found ${packagesSync.length} packages`);
// Static methods
const allPackages = await Project.getPackages("/path/to/monorepo");Access and manage project configuration from lerna.json and package.json.
/**
* Project version getter/setter
*/
get version(): string;
set version(val: string): void;
/**
* Write configuration back to file system
* @returns {Promise<string>} Path to written config file
*/
serializeConfig(): Promise<string>;
/**
* Check if project uses independent versioning
* @returns {boolean} True if independent versioning is enabled
*/
isIndependent(): boolean;Usage Examples:
const project = new Project();
// Access configuration
console.log(project.config.version); // Current version
console.log(project.config.packages); // Package patterns
console.log(project.config.useWorkspaces); // Workspace usage
// Modify and save configuration
project.version = "2.0.0";
await project.serializeConfig();
// Check versioning mode
if (project.isIndependent()) {
console.log("Using independent versioning");
}Support for extending configurations from other modules using the extends property.
/**
* Configuration can extend from locally-resolvable modules
* Supports inheritance chains with circular reference detection
*/
interface ProjectConfig {
extends?: string; // Path to module to extend from
// ... other properties
}Usage Examples:
// lerna.json
{
"extends": "@my/lerna-config",
"version": "1.0.0"
}
// The @my/lerna-config module exports:
module.exports = {
"npmClient": "yarn",
"useWorkspaces": true,
"command": {
"publish": {
"conventionalCommits": true
}
}
};
// Resulting merged configuration:
const project = new Project();
console.log(project.config);
// {
// "version": "1.0.0",
// "npmClient": "yarn",
// "useWorkspaces": true,
// "command": { "publish": { "conventionalCommits": true } }
// }Support for npm, yarn, and pnpm workspace configurations.
/**
* Get package configuration patterns
* Resolves from pnpm-workspace.yaml, package.json workspaces, or lerna.json
*/
get packageConfigs(): string[];
/**
* Get PNPM workspace configuration
*/
get pnpmWorkspaceConfig(): PnpmWorkspaceConfig;
/**
* Get package parent directories
*/
get packageParentDirs(): string[];Usage Examples:
const project = new Project();
// Get package patterns
const patterns = project.packageConfigs;
console.log(patterns); // ["packages/*", "tools/*"]
// Check workspace configuration
if (project.config.useWorkspaces) {
const workspaces = project.manifest.get("workspaces");
console.log("Using workspaces:", workspaces);
}
// PNPM-specific configuration
if (project.config.npmClient === "pnpm") {
const pnpmConfig = project.pnpmWorkspaceConfig;
console.log("PNPM packages:", pnpmConfig.packages);
}File finding, license detection, and project metadata access.
/**
* Get project root package.json as Package instance
*/
get manifest(): Package;
/**
* Get path to project license file
*/
get licensePath(): string;
/**
* Get file finder function for locating files in packages
*/
get fileFinder(): FileFinder;
/**
* Get paths to package license files
* @returns {Promise<string[]>} Array of license file paths
*/
getPackageLicensePaths(): Promise<string[]>;Usage Examples:
const project = new Project();
// Access root package.json
const rootPackage = project.manifest;
console.log(rootPackage.name); // Root package name
console.log(rootPackage.get("scripts")); // Root scripts
// Find license files
const licensePath = project.licensePath;
const packageLicenses = await project.getPackageLicensePaths();
// Use file finder to locate files across packages
const fileFinder = project.fileFinder;
const readmeFiles = await fileFinder("README.md");
const testFiles = await fileFinder("*.test.js");/**
* Project configuration object
*/
interface ProjectConfig {
version: string;
packages?: string[];
useNx?: boolean;
useWorkspaces?: boolean;
npmClient?: string;
command?: {
[commandName: string]: Record<string, any>;
};
extends?: string;
[key: string]: any; // Allow additional configuration properties
}
/**
* PNPM workspace configuration from pnpm-workspace.yaml
*/
interface PnpmWorkspaceConfig {
packages: string[];
}
/**
* File finder function for locating files in packages
* @param fileName - File name or glob pattern to search for
* @param fileMapper - Optional function to transform file paths or file path arrays
* @param customGlobOpts - Optional glob options to override defaults
* @returns Promise resolving to array of file paths, or transformed results if fileMapper is provided
*/
type FileFinder = <T = string>(
fileName: string,
fileMapper?: (filePaths: string[]) => T[] | Promise<T[]>,
customGlobOpts?: object
) => Promise<T[]>;
/**
* Package class from @lerna/package
*/
class Package {
name: string;
version: string;
location: string;
private: boolean;
get(key: string): any;
set(key: string, value: any): this;
toJSON(): object;
}The package throws ValidationError instances for configuration-related issues:
Example error handling:
const { Project } = require("@lerna/project");
const { ValidationError } = require("@lerna/validation-error");
try {
const project = new Project();
const packages = await project.getPackages();
} catch (error) {
if (error instanceof ValidationError) {
console.error("Configuration error:", error.message);
} else {
console.error("Unexpected error:", error);
}
}