@flowwright/plugin-node
The standard Node lifecycle as one call: install with a lockfile-keyed cache, the usual checks, a build, and optionally a publish.
npm install --save-dev @flowwright/plugin-node
API
| Export | Kind | Signature |
|---|---|---|
nodeStages | stage builder | (options?) => StageDefinition[] |
nodeServicePipeline | pipeline | (options?: NodePipelineOptions) => PipelineDefinition |
| Option | Default | Meaning |
|---|---|---|
packageManager | "pnpm" | npm, pnpm or yarn — picks the install command and lockfile |
cache | true | Cache node_modules, keyed by the lockfile |
install | true | false skips it; a string replaces the command |
lint, test | true | Run the matching npm script |
format, typecheck, audit | false | Off unless enabled |
build | true | Run the build script |
artifactPath | — | Declare an output after a successful build |
nodeVersion | — | Pin every stage to container: "node:<v>" |
nodeVersions | — | Fan the test stage across versions, one container each |
publish | false | Add npm publish; { provenance, access, credential } |
name | "node-service" | nodeServicePipeline only |
Every toggle takes a string instead of true to override the command:
test: "vitest run --coverage".
Example
import { pipeline, group } from "@flowwright/core";
import { nodeStages } from "@flowwright/plugin-node";
export default pipeline({
name: "acme-api",
concurrency: 4,
stages: [
...group(
"ci",
nodeStages({
packageManager: "pnpm",
typecheck: true,
nodeVersions: ["20", "22"],
artifactPath: "dist",
}),
),
],
});
What it composes down to
Defaults with pnpm:
install
$ pnpm install --frozen-lockfile
lint ← install
$ pnpm run lint
test ← install
$ pnpm run test
build ← lint, test
$ pnpm run build
The install stage restores and saves node_modules under
pnpm-deps-<hash of pnpm-lock.yaml> — the equivalent of writing:
stage("Install", {
id: "install",
run: async () => {
const key = `pnpm-deps-${await hashFiles("pnpm-lock.yaml")}`;
await cache.restore("node_modules", { key });
await sh(["pnpm", "install", "--frozen-lockfile"]);
await cache.save("node_modules", { key });
},
});
| Package manager | Install | Lockfile |
|---|---|---|
npm | npm ci | package-lock.json |
pnpm | pnpm install --frozen-lockfile | pnpm-lock.yaml |
yarn | yarn install --frozen-lockfile | yarn.lock |
Build depends on whichever checks are enabled; publish depends on build. With
nodeVersions: ["20", "22"] the single test stage becomes test-node-20 and
test-node-22, each in its own container, sharing needs: ["install"] — so they run
in parallel under -j.
Gotchas
publish always runs npm publish, even with packageManager: "pnpm" or
"yarn". That's usually what you want, but it's not derived from the setting.
nodeVersions overrides nodeVersion for the test stages — matrix stages get
node:<version> from the list, not the pinned one.
Some option combinations throw rather than doing nothing quietly:
artifactPath with build: false, cache: true with install: false, and name
passed to nodeStages() instead of nodeServicePipeline(). Defaults don't count —
nodeStages({ install: false }) is fine. See the overview.
Stage ids are unprefixed (install, lint, build), so wrap in
group() if anything else in the pipeline uses those
names.