refactor(presets/zeabur): match our current zbpack
π Linked issue
A better implementation of #3122.
Fix ZEA-7651
β Type of change
- [ ] π Documentation (updates to the documentation, readme, or JSdoc annotations)
- [x] π Bug fix (a non-breaking change that fixes an issue)
- [x] π Enhancement (improving an existing functionality like performance)
- [ ] β¨ New feature (a non-breaking change that adds functionality)
- [ ] π§Ή Chore (updates to the build process or auxiliary tools and libraries)
- [ ] β οΈ Breaking change (fix or feature that would cause existing functionality to change)
π Description
Zeabur now uses the general "node-server" to build the Dockerfile. Therefore, we simply extend "node-server" and "static".
π Checklist
- [x] I have linked an issue or discussion.
- [x] I have updated the documentation accordingly.
Checks:
- [x] Complete the tests of
zeaburandzeabur-static. - [x] "nuxt build" selects the "zeabur" provider and generates the same output as "node".
$ ZEABUR=1 pnpm build β Nitro preset: zeabur [success] [nitro] You can preview this build using `node .output/server/index.mjs` - [x] "nuxt generate" selects the ~~"zeabur-static"~~ "zeabur" provider and generates the same output as "static" (?)
$ ZEABUR=1 pnpm generate β Nitro preset: zeabur [success] [nitro] Generated public .output/public [success] [nitro] You can preview this build using `node .output/server/index.mjs`
@pan93412 is attempting to deploy a commit to the Nitro Team on Vercel.
A member of the Team first needs to authorize it.
π Walkthrough
Walkthrough
Renamed the Zeabur server preset to zeaburServer, replaced custom serverless/ISR/output hooks with extends: "node-server", updated default export to use zeaburServer, and added unit tests validating static and server preset output structures.
Changes
| Cohort / File(s) | Summary |
|---|---|
Zeabur preset refactor src/presets/zeabur/preset.ts |
Renamed zeabur β zeaburServer; removed legacy serverless/output hooks, ISR hookup, file-write and symlink side effects; simplified preset to extends: "node-server"; updated default export to use zeaburServer alongside zeaburStatic. |
Zeabur preset tests test/presets/zeabur.test.ts |
Added tests asserting output layout: the static preset produces nitro.json, a public directory and public/favicon.ico; the server preset produces a server/index.mjs entry point. |
Estimated code review effort
π― 3 (Moderate) | β±οΈ ~20 minutes
- Review the new
zeaburServerpreset to ensure required server behaviors previously provided by removed hooks are still satisfied bynode-server. - Confirm removal of ISR, file writes, and symlink logic does not break deployments or expected build outputs.
- Validate the new tests cover critical output expectations and adapt if other output artifacts are required.
Pre-merge checks and finishing touches
β Passed checks (3 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description check | β Passed | The description is directly related to the changeset, providing context about the Zeabur preset refactoring, linked issues, type of changes, and verification steps. |
| Docstring Coverage | β Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
| Title check | β Passed | The PR title follows conventional commits format with 'refactor' type and 'zeabur' scope, clearly summarizing the main change of updating the Zeabur preset. |
β¨ Finishing touches
- [ ] π Generate docstrings
π§ͺ Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
Since both of your review comments point to the same underlying issue, I'd like to provide some additional context here to address them together.
Last year, after evaluating stability and necessity, Zeabur decided to deprecate serverless runtime support, as containerization effectively covers those use cases. Currently, all projects on Zeabur are wrapped in a Dockerfile and executed as containers. As a result, the current role of zbpack is limited to:
-
Determining if the project is a Nitro project (currently hardcoded via
package.json):https://github.com/zeabur/zbpack/blob/b8d76b5758fed32203cbdbf0456a9bc5c948dfc0/internal/nodejs/plan.go#L339-L347
-
If it is, it simply starts Nitro's own
node-server:https://github.com/zeabur/zbpack/blob/b8d76b5758fed32203cbdbf0456a9bc5c948dfc0/internal/nodejs/plan.go#L713-L719
-
The
node-serverthen handles SSR and ISR. Users can choose to fully utilize SSG by specifyingZBPACK_OUTPUT_DIRand usingnuxt generateas theZBPACK_BUILD_COMMAND. We do not interfere with the build artifacts further.
You might also notice that we currently hardcode the NITRO_PRESET. Consequently, the specific build artifacts generated by the zeabur preset are actually no longer being used by our platform internally. However, this preset still impacts users who choose to write their own Dockerfiles. For example:
FROM node:25-slim AS builder
WORKDIR /src
COPY . .
RUN npm install -g pnpm && pnpm install --prod --frozen-lockfile
RUN pnpm run generate
FROM zeabur/caddy-static AS server
EXPOSE 8080
COPY --from=builder /src/.output/public /usr/share/caddy
Because Zeabur automatically injects ZEABUR=1 during CI, std-env defaults to the Zeabur preset. However, since the output path for the Zeabur preset differs from standard expectations (the artifacts end up in .zeabur/output instead of .output/public), the Dockerfile code above fails.
This has caused confusion for many users who wonder, "Why does my Dockerfile build fine locally (using node-server or static presets) but fail in the Zeabur environment?" Therefore, my goal is to align the zeabur preset behavior more closely with our current containerized environment.
If you have any better suggestions or thoughts on how to handle this, I'd love to hear them!
@pi0 sorry for pinging β what's your opinion?
Thanks for the explanations, @pan93412 β€οΈ. It makes sense if the platform is simplified by switching to a node-serverβstyle preset that also works with Dockerfile templates.
My concern is that framework detection can easily fail. In plan.go, the "nitro" (v3) NPM package is not detected (this PR is also targeting nitro v3 btw. there is not even an official Nuxt version supporting it yet, not sure how you could test). There are meta-frameworks and tools (like undocs) that use Nitro as an internal dependency, and those will fail as well. Nuxt is not the only direct dep we should be checking for.
With the current preset implementation, when a Nitro build runs in Zeabur CI, it produces a predictable output that the deployment platform can consume. I am fine if the output directory becomes .output or something else, but it should be detectable from build artifacts rather than relying on dependency-name detection for special behavior.
Vercel CLI is also detect framework by matching package:
https://github.com/vercel/vercel/blob/d4490e7ffbf47a6efb87fe3b66b6368fb0ac9d3b/packages/frameworks/src/frameworks.ts#L2365-L2395 https://github.com/vercel/vercel/blob/d4490e7ffbf47a6efb87fe3b66b6368fb0ac9d3b/packages/frameworks/src/frameworks.ts#L1520-L1588 https://github.com/vercel/vercel/blob/d4490e7ffbf47a6efb87fe3b66b6368fb0ac9d3b/packages/frameworks/src/frameworks.ts#L1908-L1944
https://github.com/vercel/vercel/blob/d4490e7ffbf47a6efb87fe3b66b6368fb0ac9d3b/packages/fs-detectors/src/detect-framework.ts
My concern is that framework detection can easily fail. In
plan.go, the"nitro"(v3) NPM package is not detected (this PR is also targeting nitro v3 btw. there is not even an official Nuxt version supporting it yet, not sure how you could test). There are meta-frameworks and tools (like undocs) that use Nitro as an internal dependency, and those will fail as well. Nuxt is not the only direct dep we should be checking for.
Currently, we can only list all frameworks based on Nitro and apply the same logic to them. Most zero-config application builders use this approach, including Vercel and Railway's Railpack. I think it is fine at the moment.
Nitro v3 will be implemented after I have cleaned up the logic in zbpack.
With the current preset implementation, when a Nitro build runs in Zeabur CI, it produces a predictable output that the deployment platform can consume. I am fine if the output directory becomes
.outputor something else, but it should be detectable from build artifacts rather than relying on dependency-name detection for special behavior.
Yes. Once we declare that a framework (e.g., Nuxt, Undocs, TanStack Start) is based on Nitro, they will all follow the same bundle logic in zbpack, as their outputs are predictable. The only thing we need to maintain is the list of Nitro frameworks.
Some providers also detect "some" Nitro based frameworks however zero config detection build output and docs are based on assumption that platform's framework detection is not a hard requirement.
Maintaining a list of known pkg names per each provider is not desired nor practical honestly. The fact that official "nitro" package itself(!) is missing today (and our e2e deploys failed without notice) is a proof of this. Anyone should be able to make a Nitro based tool that just works everywhere without waiting.
Regardless, i will properly review platform support as soon as could and likely defaulting to something guaranteed to work on zeabur even if detection fails.
Feel free to reach me out in discord (@pi0) to chat more.