Run a script defined in a package.json file (eg, vlt run and
lifecycle scripts), or an arbitrary command as if it was (eg,
vlt exec).
Usage
import { run, exec } from '@vltpkg/run'
const cwd = '/path/to/pkg'
// to run a script, as with `vlt run blah`const runResult = await run({ // the name of the thing in package.json#scripts event: 'build',
// the dir where the package.json lives cwd,
// if the script is not defined in package.json#scripts, just // ignore it and treat as success. Otherwise, treat as an // error. Default false. ignoreMissing: true,
// extra arguments to pass into the child process args: ['some', 'arguments'],
// the environment variables to add, defaults to process.env. // note that @vltpkg/run will add some of its own, as well: // - npm_lifecycle_event: the event name // - npm_lifecycle_script: the command in package.json#scripts // - npm_package_json: path to the package.json file // - VLT_* envs for all vlt configuration values that are set env: process.env,
// set this to `true` to take over the terminal and run in the // foreground, inheriting the parent process's stdio // by default, the script runs in the background. // Only one foreground:true script will be run in parallel! foreground: true,
// the shell to run the script in. Defaults to `${SHELL}` env // variable if set, otherwise the system specific shell, // `cmd.exe` on windows, and `/bin/sh` on posix. 'script-shell': '/usr/bin/bash',
// pass in a @vltpkg/package-json.PackageJson instance, and // it'll be used for reading the package.json file. Optional, // may improve performance somewhat. packageJson: new PackageJson(),})
// to execute an arbitrary command, as with `vlt exec whatever`const execResult = await exec({ // the command to execute. command: 'do-something', args: ['some', 'arguments'], // other arguments all the same.})Node-gyp Shimming
The @vltpkg/run package automatically provides node-gyp shimming for
commands that contain node-gyp references. This allows packages that
expect node-gyp to be available to work seamlessly with vlt’s
package management system.
How it works
When executing commands that contain references to node-gyp, the
package will:
- Create a
node-gypshim file in the XDG runtime directory (typically~/.run/vlt/run/node-gypon Unix or%TEMP%\xdg.run\vlt\run\node-gyp.cmdon Windows) - Inject the shim directory into the command’s
PATHenvironment variable - The shim redirects all
node-gypcalls tovlx node-gyp@latest
The shim is created once per session and cached in memory for
performance. It works for both simple commands like node-gyp rebuild
and complex commands with shell operators like
echo "before" && node-gyp rebuild && echo "after".
Cross-platform support
The shimming system is fully cross-platform:
- Unix/Linux/macOS: Creates an executable shell script with
shebang (
#!/bin/sh) - Windows: Creates a batch file (
.cmd) that forwards arguments
Examples
// These commands will automatically use the node-gyp shim:await run({ cwd: '/path/to/pkg', arg0: 'build', // where build script is: "node-gyp rebuild" projectRoot: '/path/to/pkg',})
await exec({ arg0: 'echo "before" && node-gyp rebuild && echo "after"', cwd: '/path/to/pkg', projectRoot: '/path/to/pkg', 'script-shell': true,})
await exec({ arg0: 'node-gyp', args: ['configure', '--debug'], cwd: '/path/to/pkg', projectRoot: '/path/to/pkg',})
// These commands will NOT use the shim:await exec({ arg0: 'echo "hello"', // No node-gyp reference cwd: '/path/to/pkg', projectRoot: '/path/to/pkg',})How to verify the shim
You can access the shim utilities to inspect or verify the setup:
import { getNodeGypShim, getNodeGypShimDir, hasNodeGypReference,} from '@vltpkg/run'
// Get the path to the shim fileconst shimPath = await getNodeGypShim()// e.g., '/home/user/.run/vlt/run/node-gyp'
// Get the directory containing the shim (for PATH injection)const shimDir = await getNodeGypShimDir()// e.g., '/home/user/.run/vlt/run'
// Check if a command contains node-gyp referencesconst needsShim = hasNodeGypReference('node-gyp rebuild')// trueFallback behavior
If the shim cannot be created (e.g., due to filesystem permissions),
the error is silently caught and the command executes normally. This
ensures the command fails naturally if node-gyp is actually needed
but not available, providing clear error messages to the user.