
@vltpkg/graph
This is the graph library responsible for representing the packages
that are involved in a given install.
Overview · Concepts ·
Architecture · API ·
Usage · Related Workspaces ·
References
Overview
The @vltpkg/graph workspace models a project's dependency
relationships and drives npm-compatible installs by computing how
node_modules should be structured. It exposes a public API through
src/index.ts that re-exports core types and workflows.
At a glance:
Graph encapsulates the full dependency graph for a project
(including monorepo workspaces), and is the source of truth for how
to lay out node_modules.
Node represents a unique package instance (uniqueness provided by
@vltpkg/dep-id).
Edge represents a dependency relationship from a dependent to a
dependency (eg. dependencies, devDependencies,
peerDependencies, etc.).
Diff describes the minimal set of changes required to transform an
Actual graph (disk) into an Ideal graph (desired outcome), which is
then applied by the reify subsystem.
Concepts
- Importers: Root-level nodes used as starting points of the graph.
The
mainImporter is the project root (its package.json), and the
remaining importers are workspaces discovered by
@vltpkg/workspaces.
- Hidden Lockfile: A performance optimization stored at
node_modules/.vlt-lock.json mirroring the current on-disk state to
accelerate subsequent loads of the Actual graph.
- Modifiers: Configuration for selectively altering dependency
resolution via DSS queries in
vlt.json.
- Peer Contexts: Isolation mechanism for peer dependencies that allows
multiple versions of the same package when peer requirements differ.
API
actual.load(options): Graph
Recursively loads the node_modules folder found at projectRoot in
order to create a graph representation of the current installed
packages.
ideal.build(options): Promise<Graph>
Builds the ideal dependency graph by loading from lockfile (preferred)
or actual graph, then expanding dependencies by fetching manifests.
Requires packageInfo and remover in addition to standard options.
lockfile.load(options): Graph
Loads the lockfile file found at projectRoot and returns the graph.
lockfile.save(options): void
Saves the graph to vlt-lock.json.
reify(options): Promise<ReifyResult>
Computes a Diff between the Actual and Ideal graphs and applies the
minimal filesystem changes (creating/deleting links, writing
lockfiles, hoisting, lifecycle scripts) to make the on-disk install
match the Ideal graph. Returns { diff, buildQueue }.
install(options, add?): Promise<{ graph, diff, buildQueue }>
High-level install orchestration that handles graph building, reify,
and lockfile management. Supports --frozen-lockfile,
--clean-install, and --lockfile-only modes.
mermaidOutput(graph): string
Generates Mermaid flowchart syntax from graph data.
humanReadableOutput(graph, options): string
Generates ASCII tree output with optional colors. Used in vlt ls.
jsonOutput(graph): JSONOutputItem[]
Returns array of {name, fromID, spec, type, to, overridden} items.
Usage
High-Level Install
import { install } from '@vltpkg/graph'
const { graph, diff, buildQueue } = await install({
projectRoot: process.cwd(),
packageInfo,
packageJson,
scurry,
allowScripts: '*',
})
Load Actual Graph and Reify
import { actual, ideal, reify } from '@vltpkg/graph'
import { RollbackRemove } from '@vltpkg/rollback-remove'
const remover = new RollbackRemove()
const from = actual.load({
projectRoot: process.cwd(),
packageJson,
scurry,
loadManifests: true,
})
const to = await ideal.build({
projectRoot: process.cwd(),
packageInfo,
packageJson,
scurry,
remover,
})
const { diff, buildQueue } = await reify({
graph: to,
actual: from,
packageInfo,
packageJson,
scurry,
remover,
allowScripts: '*',
})
Working With Lockfiles
import { lockfile } from '@vltpkg/graph'
const graph = lockfile.load({
projectRoot,
mainManifest,
packageJson,
})
lockfile.save({ graph })
Graph Visualization
import {
mermaidOutput,
humanReadableOutput,
jsonOutput,
} from '@vltpkg/graph'
const mermaid = mermaidOutput({
edges: [...graph.edges],
nodes: [...graph.nodes.values()],
importers: graph.importers,
})
const tree = humanReadableOutput(
{
edges: [...graph.edges],
nodes: [...graph.nodes.values()],
importers: graph.importers,
},
{ colors: true },
)
const json = jsonOutput({
edges: [...graph.edges],
nodes: [...graph.nodes.values()],
importers: graph.importers,
})
Architecture
Graph construction modes supported by the library:
-
Virtual Graphs (lockfile-based)
- Load and save via
src/lockfile/load.ts and
src/lockfile/save.ts
- Hidden lockfile:
node_modules/.vlt-lock.json for faster loads
- 📖 Lockfile README
-
Actual Graphs (filesystem-based)
- Loaded by traversing
node_modules via src/actual/load.ts
- May shortcut to Hidden Lockfile if present and valid
- File layout changes are performed by
src/reify/
-
Ideal Graphs (desired end state)
- Entry:
src/ideal/build.ts
- Starts from Virtual (preferred) or falls back to Actual
- Merges
add/remove input with importer manifests using
src/ideal/get-importer-specs.ts
- Fetches and expands manifests using
@vltpkg/package-info, reuses
existing nodes that satisfy specs
- 📖 Ideal README
Finally, src/diff.ts computes changes and src/reify/ applies them
to the filesystem.
Related Workspaces
@vltpkg/dep-id: Unique IDs for packages, ensuring Node identity
@vltpkg/spec: Parse/normalize dependency specifiers and registry
semantics
@vltpkg/semver: Semantic version parsing/comparison
@vltpkg/satisfies: Check if a DepID satisfies a Spec
@vltpkg/package-info: Fetch remote manifests and artifacts
(registry, git, tarball)
@vltpkg/package-json: Read and cache local package.json files
@vltpkg/workspaces: Monorepo workspace discovery and grouping
@vltpkg/rollback-remove: Safe file removal with rollback
capability
@vltpkg/vlt-json: Load vlt.json configuration (modifiers, etc.)
References