'New Feature — Selectors Everywhere: Commands now support powerful dependency filtering across your projects. Example command: vlr --scope='[name^=devtools-]' test.'
Published on

Query Powered vlt Commands

Authors

Target your full graph with --scope support for commands like vlr, vlx, and vlt pkg

Running scripts across a large project just got a lot more precise. vlr (vlt run), vlx (vlt exec) and vlt pkg now support the full Dependency Selector Syntax via the --scope flag. You can now target exactly the dependencies you want to take actions against: specific workspaces, prod or dev-only deps, git deps with a prepare script, and more.

If you're familiar with CSS selectors, you'll feel right at home. The --scope flag accepts the complete Dependency Selector Syntax.

tl;dr

  • vlr --scope="<selector>" <script> runs <script> in every package matched by <selector>.
  • vlx --scope="<selector>" <command> executes <command> in every package matched by <selector>.
  • vlt pkg get [name] --scope="<selector>" will get package information for all packages matched by <selector>.
  • Works great with workspaces as well as any and all other dependencies in your project (including transitives).

Why this is useful

As your projects grow, and especially as monorepos become more and more popular, developers need smarter tools to control the execution of tasks across their dependency graphs. Making our commands configurable using the Dependency Selector Syntax allows developers and teams to:

  • Run scripts only where there's compatibility: e.g., test workspaces whose engines.node support is for a specific version.
  • Target non-workspace dependencies: e.g. running postinstall scripts for select transitive deps who you may have been previously blocked and need to rehydrate or diagnose environment-specific behavior.
  • Target by metadata: e.g. limit the execution of a tool against just git, :prod, :dev, specifically named, pathed, or security-related dependencies.

How it works

vlt evaluates the selector passed with --scope against your project's dependency graph. Any matching dependency will be returned and the corresponding command/action will be taken (ex. script execution for vlr, binary execution for vlx, package.json value fetching for vlt pkg get etc.). Selectors work to match configured workspaces, direct dependencies and even transitive dependencies.

The Dependency Selector Syntax supports:

  • Attribute selectors like [name], [name=react], [license^=MIT]
  • Property selectors like :attr(engines,[node^=20]) or :attr(deeply,nested,prop,[name=value])
  • Combinators like > (direct deps) and the descendant combinator (all transitive deps)
  • Pseudo Classes like :has(...), :is(...), :attr(...), :semver(...), :path("..."), :type(git|file|workspace|...)
  • Pseudo States like :workspace, :project, :prod, :dev, :peer, :private

For the full reference, check the docs: Dependency Selector Syntax.

Examples

Run tests for workspaces that support a specific Node version

$ vlr --scope=":workspace:semver($(node -v), satisfies, :attr(engines, [node]))" test
> app-web ok
vitest run
> api-server test
node --test

Run the postinstall script for all production dependencies that declare one

$ vlr --scope=":prod:attr(scripts, [postinstall])" postinstall
> esbuild postinstall
node install.js
> sharp postinstall
node install/libvips

Run the prepare script for any git dependencies

$ vlr --scope=":type(git):attr(scripts, [prepare])" prepare
> my-git-dep prepare
tshy build
> another-git-dep prepare
tsc -b

Lint workspaces whose names start with app-

$ vlr --scope=":workspace[name^=app-]" lint
> app-shell lint
eslint .
> app-admin lint
eslint .

Run the test scripts of workspaces which defined them in the packages/** directory

$ vlr --scope=':path("packages/**"):attr(scripts, [test])' test
> packages/foo test
vitest run
> packages/bar test
vitest run

Run the build script for specifically named workspaces

$ vlr --scope=":workspace:is([name=web],[name=docs],[name=api])" build
> web build
next build
> docs build
static-site build
> api build
tsc -b && node scripts/build.js

Execute tsc for workspaces that have typescript as a dev dependency

$ vlx --scope ":workspace:attr(devDependencies, [typescript])" tsc --noEmit
> app-web
tsc --noEmit
> app-server
tsc --noEmit

Execute vercel for any projects found in the ./apps directory

$ vlx --scope=':path("apps/*")' vercel
> apps/web
vercel
> apps/api
vercel

Inspect package.json fields with vlt pkg

Use vlt pkg get to fetch a single value and vlt pkg pick to fetch multiple values (supports dot-prop paths). These commands work with --scope, so you can query fields across workspaces or any part of your dependency graph. See docs: vlt pkg.

$ vlt pkg get name --scope=":workspace"
app-web
app-server

$ vlt pkg get engines.node --scope=":workspace:attr(engines, [node])"
^22 || ^24
^18 || ^20

$ vlt pkg pick name version repository.url --scope=":is([name=web],[name=api])"
[
  {
    "name": "web",
    "version": "1.0.0",
    "repository": {
      "url": "https://github.com/vltpkg/web"
    }
  },
  {
    "name": "api",
    "version": "2.0.0",
    "repository": {
      "url": "https://github.com/vltpkg/api"
    }
  }
]

$ vlt pkg pick name scripts.test --scope=':path("packages/**"):attr(scripts, [test])'
[
  {
    "name": "app-web",
    "scripts": {
      "test": "vitest run"
    }
  },
  {
    "name": "app-server",
    "scripts": {
      "test": "node --test"
    }
  }
]

Learn more

The Dependency Selector Syntax is powerful, expressive and improving all the time. You can read the complete reference documentation here.

If you have any questions, comments or feedback about the selector syntax, command support for --scope, or anything vlt-related, come join us on Discord or open an issue on GitHub.

Join the waitlist

Curious to learn more about vlt? Join our waitlist and get early access.

Sign up