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 { justBash } from "codelet/extensions/terminal/just-bash";

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

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 — just-bash or webcontainer. Mount npm with no shell in the workbench and every task refuses to run: the reader sees an error message, and executeTask rejects.

just-bash opens a shell, but it has no package manager installed in it — a script run there fails with "command not found" rather than actually building anything. webcontainer is real node with npm, pnpm, yarn and bun all on its PATH. If your workbench mounts both, tell npm which one to use (see Options) — the shells are not interchangeable for this.

#Options

OptionTypeDefaultDescription
shellstringThe id of a contributes.terminal.profiles entry to run scripts in, "webcontainer.shell" among them.
npm({ shell: "webcontainer.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 webcontainer.shell is safe even before cross-origin isolation makes it available: the task runs on whatever shell is there once it is.

#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.