Skip to main content

@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

ExportKindSignature
nodeStagesstage builder(options?) => StageDefinition[]
nodeServicePipelinepipeline(options?: NodePipelineOptions) => PipelineDefinition
OptionDefaultMeaning
packageManager"pnpm"npm, pnpm or yarn — picks the install command and lockfile
cachetrueCache node_modules, keyed by the lockfile
installtruefalse skips it; a string replaces the command
lint, testtrueRun the matching npm script
format, typecheck, auditfalseOff unless enabled
buildtrueRun the build script
artifactPathDeclare an output after a successful build
nodeVersionPin every stage to container: "node:<v>"
nodeVersionsFan the test stage across versions, one container each
publishfalseAdd 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

pipeline.ts
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 managerInstallLockfile
npmnpm cipackage-lock.json
pnpmpnpm install --frozen-lockfilepnpm-lock.yaml
yarnyarn install --frozen-lockfileyarn.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.