From c95464363aa7165a01599eab26ad301988316ec6 Mon Sep 17 00:00:00 2001 From: Elizabeth Craig Date: Tue, 3 Feb 2026 11:08:50 -0800 Subject: [PATCH] Cleanup and docs for graph APIs --- ...-0a09b50b-4b3a-43b5-9864-213ff22b21e7.json | 11 ++++ .../etc/workspace-tools.api.md | 20 ++---- .../src/graph/createDependencyMap.ts | 16 ++++- .../src/graph/createPackageGraph.ts | 18 +++--- .../src/graph/getPackageDependencies.ts | 61 +++++-------------- packages/workspace-tools/src/index.ts | 10 ++- .../workspace-tools/src/types/PackageGraph.ts | 8 ++- 7 files changed, 65 insertions(+), 79 deletions(-) create mode 100644 change/change-0a09b50b-4b3a-43b5-9864-213ff22b21e7.json diff --git a/change/change-0a09b50b-4b3a-43b5-9864-213ff22b21e7.json b/change/change-0a09b50b-4b3a-43b5-9864-213ff22b21e7.json new file mode 100644 index 00000000..8e862970 --- /dev/null +++ b/change/change-0a09b50b-4b3a-43b5-9864-213ff22b21e7.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "type": "patch", + "comment": "Cleanup and docs for graph APIs", + "packageName": "workspace-tools", + "email": "elcraig@microsoft.com", + "dependentChangeType": "patch" + } + ] +} \ No newline at end of file diff --git a/packages/workspace-tools/etc/workspace-tools.api.md b/packages/workspace-tools/etc/workspace-tools.api.md index 928dc72f..244c9451 100644 --- a/packages/workspace-tools/etc/workspace-tools.api.md +++ b/packages/workspace-tools/etc/workspace-tools.api.md @@ -53,10 +53,10 @@ export function commit(options: GitCommitOptions): void; // @public @deprecated (undocumented) export function commit(message: string, cwd: string, options?: string[]): void; -// @public (undocumented) +// @public export function createDependencyMap(packages: PackageInfos, options?: PackageDependenciesOptions): DependencyMap; -// @public (undocumented) +// @public export function createPackageGraph(packages: PackageInfos, filters?: PackageGraphFilter[] | PackageGraphFilter): PackageGraph; // @public (undocumented) @@ -66,9 +66,7 @@ export type Dependencies = { // @public (undocumented) export interface DependencyMap { - // (undocumented) dependencies: Map>; - // (undocumented) dependents: Map>; } @@ -538,34 +536,24 @@ export interface PackageDependenciesOptions { // @public export interface PackageDependency { - // (undocumented) dependency: string; - // (undocumented) name: string; } // @public export interface PackageGraph { - // (undocumented) dependencies: PackageDependency[]; - // (undocumented) packages: string[]; } -// @public (undocumented) -export interface PackageGraphFilter { +// @public +export interface PackageGraphFilter extends PackageDependenciesOptions { // (undocumented) includeDependencies?: boolean; // (undocumented) includeDependents?: boolean; // (undocumented) namePatterns: string[]; - // (undocumented) - withDevDependencies?: boolean; - // (undocumented) - withOptionalDependencies?: boolean; - // (undocumented) - withPeerDependencies?: boolean; } // @public diff --git a/packages/workspace-tools/src/graph/createDependencyMap.ts b/packages/workspace-tools/src/graph/createDependencyMap.ts index 533040b8..9281c3cc 100644 --- a/packages/workspace-tools/src/graph/createDependencyMap.ts +++ b/packages/workspace-tools/src/graph/createDependencyMap.ts @@ -2,17 +2,27 @@ import { getPackageDependencies, PackageDependenciesOptions } from "./getPackage import type { PackageInfos } from "../types/PackageInfo"; export interface DependencyMap { + /** Mapping from package names to their dependencies */ dependencies: Map>; + /** Mapping from package names to their dependents */ dependents: Map>; } +/** + * Creates a dependency map for the packages in a monorepo. + * + * @param packages - Information about all packages in the monorepo + * @param options - Which dependency types to include. `dependencies` are always included, and it + * defaults to also including `devDependencies`. + * @returns A map of package dependencies and dependents + */ export function createDependencyMap( packages: PackageInfos, options: PackageDependenciesOptions = { withDevDependencies: true, withPeerDependencies: false } ): DependencyMap { - const map = { - dependencies: new Map>(), - dependents: new Map>(), + const map: DependencyMap = { + dependencies: new Map(), + dependents: new Map(), }; const internalPackages = new Set(Object.keys(packages)); diff --git a/packages/workspace-tools/src/graph/createPackageGraph.ts b/packages/workspace-tools/src/graph/createPackageGraph.ts index feb3ab06..ab15f1b8 100644 --- a/packages/workspace-tools/src/graph/createPackageGraph.ts +++ b/packages/workspace-tools/src/graph/createPackageGraph.ts @@ -1,18 +1,17 @@ +import micromatch from "micromatch"; +import type { PackageGraph } from "../types/PackageGraph"; import type { PackageInfos } from "../types/PackageInfo"; import type { DependencyMap } from "./createDependencyMap"; -import type { PackageGraph } from "../types/PackageGraph"; - import { createDependencyMap } from "./createDependencyMap"; -import micromatch from "micromatch"; +import type { PackageDependenciesOptions } from "./getPackageDependencies"; -// Reference: https://github.com/pnpm/pnpm/blob/597047fc056dd25b83638a9ab3df0df1c555ee49/packages/filter-workspace-packages/src/parsePackageSelector.ts -export interface PackageGraphFilter { +/** + * Reference: https://github.com/pnpm/pnpm/blob/597047fc056dd25b83638a9ab3df0df1c555ee49/packages/filter-workspace-packages/src/parsePackageSelector.ts + */ +export interface PackageGraphFilter extends PackageDependenciesOptions { namePatterns: string[]; includeDependencies?: boolean; includeDependents?: boolean; - withDevDependencies?: boolean; - withPeerDependencies?: boolean; - withOptionalDependencies?: boolean; } /** Package graph visitor is called as it visits every package in dependency order */ @@ -20,6 +19,9 @@ interface PackageGraphVisitor { (pkg: string, dependencies: string[], dependents: string[]): void; } +/** + * Create a package graph for the given packages, optionally filtered by the provided filters. + */ export function createPackageGraph( packages: PackageInfos, filters?: PackageGraphFilter[] | PackageGraphFilter diff --git a/packages/workspace-tools/src/graph/getPackageDependencies.ts b/packages/workspace-tools/src/graph/getPackageDependencies.ts index be325d7d..df134438 100644 --- a/packages/workspace-tools/src/graph/getPackageDependencies.ts +++ b/packages/workspace-tools/src/graph/getPackageDependencies.ts @@ -6,28 +6,11 @@ export interface PackageDependenciesOptions { withOptionalDependencies?: boolean; } -/** - * Verify that `dep`'s version is not specified with `npm:` or `file:` protocol. - */ -function isValidDependency(info: PackageInfo, dep: string): boolean { - // check if the dependency range is specified by an external package like npm: or file: - const range = - info.dependencies?.[dep] || - info.devDependencies?.[dep] || - info.peerDependencies?.[dep] || - info.optionalDependencies?.[dep]; - - // this case should not happen by this point, but we will handle it anyway - if (!range) { - return false; - } - - return !range.startsWith("npm:") && !range.startsWith("file:"); -} +type DependencyType = "dependencies" | "devDependencies" | "peerDependencies" | "optionalDependencies"; /** * Gets the monorepo package dependencies for a given package (excluding `file:` or `npm:` versions). - * It only considers `dependencies` unless options specify otherwise. + * It only considers `dependencies` and `devDependencies` unless options specify otherwise. * * @param info - The package information containing dependencies * @param internalPackages - Set of in-repo package names to consider. @@ -39,41 +22,25 @@ export function getPackageDependencies( internalPackages: Set, options: PackageDependenciesOptions = { withDevDependencies: true } ): string[] { - const deps: string[] = []; + const depTypes: DependencyType[] = ["dependencies"]; + options.withDevDependencies && depTypes.push("devDependencies"); + options.withPeerDependencies && depTypes.push("peerDependencies"); + options.withOptionalDependencies && depTypes.push("optionalDependencies"); - if (info.dependencies) { - for (const dep of Object.keys(info.dependencies)) { - if (dep !== info.name && internalPackages.has(dep)) { - deps.push(dep); - } - } - } - - if (info.devDependencies && options.withDevDependencies) { - for (const dep of Object.keys(info.devDependencies)) { - if (dep !== info.name && internalPackages.has(dep)) { - deps.push(dep); - } - } - } + const deps: string[] = []; - if (info.peerDependencies && options.withPeerDependencies) { - for (const dep of Object.keys(info.peerDependencies)) { - if (dep !== info.name && internalPackages.has(dep)) { - deps.push(dep); - } + for (const depType of depTypes) { + const dependencies = info[depType]; + if (!dependencies) { + continue; } - } - if (info.optionalDependencies && options.withOptionalDependencies) { - for (const dep of Object.keys(info.optionalDependencies)) { - if (dep !== info.name && internalPackages.has(dep)) { + for (const [dep, range] of Object.entries(dependencies)) { + if (dep !== info.name && internalPackages.has(dep) && !range.startsWith("npm:") && !range.startsWith("file:")) { deps.push(dep); } } } - const filteredDeps = deps.filter((dep) => isValidDependency(info, dep)); - - return filteredDeps; + return deps; } diff --git a/packages/workspace-tools/src/index.ts b/packages/workspace-tools/src/index.ts index 6ccdf5d5..8e355293 100644 --- a/packages/workspace-tools/src/index.ts +++ b/packages/workspace-tools/src/index.ts @@ -1,6 +1,12 @@ -export * from "./dependencies/index"; +export { + getTransitiveDependencies, + getTransitiveDependents, + getInternalDeps, + getTransitiveConsumers, + getTransitiveProviders, +} from "./dependencies/index"; export { getPackageInfos, getPackageInfosAsync } from "./getPackageInfos"; -export * from "./git"; +export * from "./git/index"; export * from "./graph/index"; export { setCachingEnabled } from "./isCachingEnabled"; export * from "./lockfile"; diff --git a/packages/workspace-tools/src/types/PackageGraph.ts b/packages/workspace-tools/src/types/PackageGraph.ts index 45f15ce6..b04fb615 100644 --- a/packages/workspace-tools/src/types/PackageGraph.ts +++ b/packages/workspace-tools/src/types/PackageGraph.ts @@ -1,14 +1,16 @@ /** A package graph edge that defines a single package name and one of its dependency */ export interface PackageDependency { + /** Name of a package */ name: string; + /** A dependency of `name` */ dependency: string; } -/** The graph is defined by as a list of package names as nodes, and a list of PackageDependency as edges*/ +/** The graph is defined by as a list of package names as nodes, and a list of dependencies as edges */ export interface PackageGraph { - // Nodes + /** Nodes: list of package names */ packages: string[]; - // Edges + /** Edges: list of package dependencies */ dependencies: PackageDependency[]; }