vlt / docs

  • PricingBenchmarks (opens in new window)Community (opens in new window)Feedback
  • Why vlt
  • Use cases
  • npm interoperability
  • Reference

Registry
  • Quick Start
  • Using Packages
  • Membership & Access
  • Dashboard
  • Authentication & Tokens

Client
  • Getting Started
  • Configuring
  • Authentication
  • Registries
  • Security
  • Workspaces
  • Affected
  • Catalogs
  • Peer Dependencies
  • Graph Modifiers
  • Global Installs
  • Overview
  • Understanding how Graph Modifiers work
  • Usage
  • Basic Overrides
  • Universal Selector
  • Path-Specific Selector
  • Removing Dependencies
  • Peer Dependencies
  • Adding Comments to Selectors
  • Specificity Resolution
  • Semver Pseudo-Selectors
  • Integration with Catalogs
  • Using with workspaces
  • Implicit workspace references
  • Best Practices
  • See Also
  1. Client
  2. /
  3. Graph Modifiers

Graph Modifiers

Graph Modifiers are a recent addition to the vlt client that allow you to dynamically alter and refine how the package dependency graph is traversed and evaluated. This feature gives you granular control over installed packages, including their transitive dependencies, letting you target exactly which parts of the graph to operate on.


Overview

Graph Modifiers extend the existing dependency graph traversal logic in vlt using our Dependency Selector Syntax. These selectors let you modify the behavior of an existing vlt install operation, allowing you to target specific packages and their dependencies.

Note: In its initial release, Graph Modifiers support a limited subset of the Dependency Selector Syntax:

  • :root
  • :project
  • :workspace
  • ID selectors (#package-name)
  • Semantic version selectors (:v(^1.0.0))
  • Direct dependency combinator (>)
  • :workspace as the last selector, right after :root >, :project > or :workspace >, matching any direct dependency named after one of the project's workspaces (see Implicit workspace references)

Understanding how Graph Modifiers work

User-defined Graph Modifier entries will be read and applied during the graph build phase. In order to apply new modifier entries the graph build will be retriggered every time the "modifiers" property is changed in the vlt.json file


Usage

Graph Modifiers are configured via the modifiers field in your vlt.json configuration file. Below are some examples on how to use it:

Basic Overrides

JSON
{
  "modifiers": {
    ":root > #express": "^4.18.0",
    "#lodash": "^4.17.21"
  }
}

The keys are selectors that identify which dependency to override, and the values specify the versions to enforce.

Universal Selector

JSON
{
  "modifiers": {
    "#react": "^18.2.0"
  }
}

Replaces every occurrence of react in the dependency tree with version ^18.2.0.

Path-Specific Selector

JSON
{
  "modifiers": {
    ":root > #next > #react": "^17.0.0"
  }
}

Only overrides react when it’s a direct dependency of next, which is a direct dependency of the root project.

Removing Dependencies

JSON
{
  "modifiers": {
    ":root > #left-pad": "-",
    "#some-pkg > #unwanted-dep": "-"
  }
}

The value "-" removes the selected dependency: it is not resolved, installed or saved to vlt-lock.json, nor is anything only it pulled in. Its package.json entry is left as is. An explicit vlt install left-pad fails with a config error until the modifier is removed. Normal selector rules apply: the most specific matching modifier wins, whatever its value (see Specificity Resolution).

Peer Dependencies

JSON
{
  "modifiers": {
    "#eslint-plugin-import > #eslint": "^8 || ^9 || ^10"
  }
}

Modifiers apply to peer dependencies too: the peer edge resolves with the modified spec. Here the eslint peer range of eslint-plugin-import is widened, so the project's eslint 10 satisfies it.

Adding Comments to Selectors

JSON
{
  "modifiers": {
    "/* pin down exact version */ :root > #express > #body-parser > #qs": "=6.10.0"
  }
}

Inline comments can be added to selector keys for clarity.

Specificity Resolution

JSON
{
  "dependencies": {
    "express": "^4.17.0"
  },
  "modifiers": {
    ":root > #express > #cookie": "^0.5.0",
    "#cookie": "^0.4.0"
  }
}

CSS-like specificity rules determine which modifier applies when multiple selectors match the same dependency. Here, the more specific :root > #express > #cookie takes precedence over #cookie. Specificity only decides between modifiers that select the same dependency: modifiers sharing part of a path, like #react-dom > #scheduler and #react-dom > #loose-envify, each apply to their own target.

Semver Pseudo-Selectors

JSON
{
  "modifiers": {
    "#lodash:v(^4.0.0)": "^4.17.21"
  }
}

This only affects lodash dependencies matching the semver range ^4.0.0.

Integration with Catalogs

JSON
{
  "catalogs": {
    "tools": {
      "react": "^18.2.0"
    }
  },
  "modifiers": {
    "#react": "catalog:tools"
  }
}

It's possible to point to configured catalogued versions from modifiers.

Using with workspaces

JSON
{
  "workspaces": "packages/*",
  "modifiers": {
    ":workspace > #typescript": "^5.0.0",
    "#core:workspace > #typescript": "~5.1.0"
  }
}

You may use the :workspace selector to target workspaces. In the example above we target a specific workspace named core by combining the #core ID selector with the :workspace selector.

Implicit workspace references

JSON
{
  "modifiers": {
    ":project > :workspace": "workspace:*"
  }
}

For monorepos whose package.json files reference local packages by plain ranges (npm, yarn v1, pnpm with link-workspace-packages) instead of the workspace: protocol. It goes in the same vlt.json that vlt install saves the workspaces to (see Saving workspaces to vlt.json):

  • Without it, a plain range links a workspace only while the workspace version satisfies that range, otherwise vlt fetches that name from the registry.
  • With it, every direct dependency of the root or of a workspace that is named after a workspace links to that workspace, whatever range is declared.
  • Other dependencies, including transitive ones, are left untouched.
  • Workspaces without a name are never matched.
  • Use :root > :workspace to limit it to the root's dependencies.
  • Explicit #name keys, e.g. ":workspace > #core": "workspace:^1", take precedence over it.
  • Only the <importer> > :workspace form is supported, no deeper paths or chained pseudo selectors.
  • Remove it once your package.json files use workspace: specs.

Best Practices

  • Start Small: Test modifiers incrementally to understand how you are modifying your install,
  • Combine wisely: Use path-specific and universal selectors to control installations accurately.

See Also

  • Workspaces
  • Catalogs

Graph Modifiers enable precise and efficient package management in vlt. Future updates will expand selector support and add advanced capabilities.


PreviousPeer DependenciesNextGlobal Installs

On this page

  • Overview
  • Understanding how Graph Modifiers work
  • Usage
  • Basic Overrides
  • Universal Selector
  • Path-Specific Selector
  • Removing Dependencies
  • Peer Dependencies
  • Adding Comments to Selectors
  • Specificity Resolution
  • Semver Pseudo-Selectors
  • Integration with Catalogs
  • Using with workspaces
  • Implicit workspace references
  • Best Practices
  • See Also
Edit this page

Deploy your package on vlt.io

Publish scoped and private packages, manage organizations and access, and give every developer and CI environment a consistent source for public and private JavaScript dependencies.

Publish now