@cfxlabsinc/nx-open-nextWorkspace-internal Nx plugin for the Next.js apps packaged with OpenNext.
createNodesV2 watches **/open-next.config.ts and contributes four targets:
| Target | Command | Notes |
|---|---|---|
build |
next build |
Cached. dependsOn: ["typecheck", "^build"], outputs dist/<root>/.next |
open-next-build |
open-next build |
Not cached — see below |
serve |
next dev / next build && next start |
development (default) and production configurations |
analyze-bundle |
bun next experimental-analyze |
build sets tty: false because Next renders a progress UI on a TTY that corrupts Nx's captured output.
typecheck, and why the plugin has to supply it@nx/js/typescript hard-excludes any project directory containing a next.config.* — before it inspects the tsconfig at all:
// Do not create project for Next.js projects since they are not compatible with
// project references and typecheck will fail.
if (siblingFiles.includes('next.config.mjs') || …) return false;
So no amount of restructuring tsconfig.json into the TS-solution layout gets a dashboard a typecheck target. And because build declares dependsOn: ["typecheck", …] while a dependsOn naming an absent target is skipped silently, the dashboards were type-checked by no Nx task at all — only indirectly by next build, at the end of a full build.
This target fills the gap with a plain --noEmit run, which sidesteps the incompatibility Nx is avoiding: no --build, no project references.
--composite false (plus the declaration flags it implies) is required because tsconfig.base.json enables composite for the libraries, and under composite TS demands every imported file appear in include — which fails on the dashboards' own _components/*Selector.tsx. Nothing references an app, so composite buys them nothing.dependsOn: ["^typecheck"] is load-bearing: the tsconfig references sibling packages, so their .d.ts must exist under dist/out-tsc first. On a clean checkout without it, ~2250 TS6305: Output file … has not been built errors — invisible locally, where that directory is warm.open-next-build keeps cache: false as it was before: it consumes the .next directory build produced and writes .open-next, which is not declared as an output — caching it would let Nx report success while leaving no artifact on disk.
open-next.config.ts is the markerNot next.config.*: the marker has to mean "a Next app that OpenNext packages for a Lambda/CloudFront deploy", which is what makes open-next-build meaningful at all. A plain Next app would want build and serve but not that. Today the file sits in admin-dashboard, bank-dashboard and customer-dashboard — the same three that carry an sst.config.ts, which @cfxlabsinc/nx-sst keys on for deploy/cancel.
serve takes the port as an nx:run-commands option rather than baking it into the command, so a project overrides just the number:
"serve": { "options": { "port": 3100 } }
Nx merges a project.json target's options into the inferred ones key by key, so cwd and both configurations survive — the override is only the port. It applies to development and production alike, since both interpolate {args.port}.
The default is 3000 (Next's own default), and can be changed workspace-wide via the plugin's defaultPort option in nx.json:
{ "plugin": "@cfxlabsinc/nx-open-next", "options": { "defaultPort": 4000 } }
This replaced customer-dashboard's hand-written serve, which existed solely to pass --port 3000. Each dashboard now declares its own port, so all three can run at once:
| Project | Port |
|---|---|
customer-dashboard |
3000 |
bank-dashboard |
3200 |
admin-dashboard |
4200 |
Registered in workspace nx.json under plugins:
{
"plugin": "@cfxlabsinc/nx-open-next",
"options": {}
}
Resolves through the workspace dependency in the root package.json:
"@cfxlabsinc/nx-open-next": "workspace:*"