Codelet logoCodelet

npm Scripts

The workspace's package.json scripts as runnable tasks.

npm reads the scripts in the workspace's package.json and turns each one into a task: a name, the package manager as its source, and the script's own command line as its detail.

import { Workbench, FileSystem } from "codelet/workbench";
import { npm } from "codelet/extensions/npm";
import { terminal } from "codelet/extensions/terminal";
import { bash } from "codelet/extensions/bash";

new Workbench({
  parent: document.getElementById("app")!,
  fs: new FileSystem({
    "/package.json": JSON.stringify({ scripts: { build: "tsc", test: "vitest run" } }),
  }),
  extensions: [npm(), terminal(), bash()],
});

Warning

A page cannot spawn a process, so running a script means typing its command line into a terminal another extension contributed. npm needs codelet/extensions/terminal plus a shell mounted alongside it — bash. Mount npm with no shell in the workbench and every task refuses to run: the reader sees an error message, and executeTask rejects.

bash has npm, npx, pnpm and yarn in it wherever the page is cross-origin isolated, and none of them where it is not — a script run there fails with "command not found" rather than actually building anything, which is the page saying it has no machine. If your workbench mounts more than one shell, tell npm which to use (see Options).

#Options

OptionTypeDefaultDescription
shellstring—The id of a contributes.terminal.profiles entry to run scripts in, "bash.shell" among them.
npm({ shell: "bash.shell" });

Without shell, the last contributed shell profile is used — the shell a host mounted last is taken as the more deliberate choice. A named shell that isn't offered falls back the same way, so naming bash.shell is safe even in a workbench that has not mounted it: the task runs on whatever shell is there.

#Choosing a package manager

The manager for a task's command line is worked out from the workspace, in order:

The packageManager field in package.json, if the tree declares one ("pnpm@11.20.0" reads as pnpm).
Otherwise, whichever lockfile is at the workspace root: pnpm-lock.yaml, yarn.lock, bun.lock or bun.lockb, then package-lock.json, first match wins.
npm, if none of the above is there.

Each task's command line is <manager> install && <manager> run <script> — install runs first, every time. A shell in a page is either a container that just booted or a tree that arrived over a tarball, so node_modules is rarely already there, and every manager's install is close to a no-op once it is.

#What it adds

One vscode.Task per script in package.json, plus an npm: Run Script command in the command palette that lists them in a quick pick and runs the one you choose. Nothing is held between fetches: package.json is read fresh each time, so a script you just added or renamed shows up the next time you open the palette or the Tasks pane — no reload needed. A workspace with no package.json, or one that fails to parse, simply has no npm tasks.

A script named exactly build, test or clean is tagged with the matching task group; any other name is left ungrouped.

#Running one

Mount codelet/extensions/tasks for a pane listing every script with a click to run it, or reach for the npm: Run Script command in the palette. Both go through the same vscode.tasks API, so a second extension that also provides "npm"-typed tasks shows up alongside these in either place.

Read more in API > Integrations.