fix: flush class manifest on SSR renderStart/generateBundle#43
fix: flush class manifest on SSR renderStart/generateBundle#43jrmybtlr wants to merge 6 commits into
Conversation
transformIndexHtml is client-only, so SSR builds could keep a stale .classy manifest through the first server render. Flush after transforms via renderStart and generateBundle, and write immediately during builds instead of relying on the debounced path. Co-authored-by: Jeremy Butler <jrmybtlr@users.noreply.github.com>
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
useclassy-com | 7aac889 | Commit Preview URL Branch Preview URL |
Jul 21 2026, 09:58 PM |
|
@copilot resolve the merge conflicts in this pull request |
…obustness-184b # Conflicts: # tasks/todo.md
Merged |
There was a problem hiding this comment.
Pull request overview
This PR makes the useClassy Vite plugin SSR-build aware by adding explicit manifest flush points during the Rollup/Vite build pipeline so .classy/output.classy.html can’t remain stale for SSR/server output.
Changes:
- Add
flushManifest()and invoke it fromrenderStart,generateBundle, andbuildEndto cover SSR builds that don’t runtransformIndexHtml. - Change write scheduling so builds use immediate writes (vs. the existing debounced path in dev).
- Add unit tests covering the new hooks and SSR/dev behavior; update lockfile for dependency/tooling changes.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/index.ts | Adds SSR/build flush hooks and refactors manifest writing into helper functions. |
| src/tests/index.test.ts | Adds tests intended to validate the new flush hooks and dev no-op behavior. |
| package-lock.json | Updates lockfile for version/dependency/tooling changes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /** Prefer an immediate write during builds so debounce cannot leave the file stale. */ | ||
| function scheduleManifestWrite(): void { | ||
| if (isBuild) { | ||
| writeDirect( | ||
| allClassesSet, | ||
| outputDir, | ||
| outputFileName, | ||
| manifestRoot, | ||
| ) | ||
| return | ||
| } | ||
|
|
||
| writeDebounced( | ||
| allClassesSet, | ||
| outputDir, | ||
| outputFileName, | ||
| manifestRoot, | ||
| ) | ||
| } |
| it('should flush the manifest on renderStart during SSR builds', async () => { | ||
| const fs = await import('fs') | ||
| const writeSpy = vi.spyOn(fs.default, 'writeFileSync').mockImplementation(() => undefined) | ||
| const renameSpy = vi.spyOn(fs.default, 'renameSync').mockImplementation(() => undefined) | ||
| ;(fs.default.existsSync as unknown as ReturnType<typeof vi.fn>).mockReturnValue(false) | ||
| ;(fs.default.mkdirSync as unknown as ReturnType<typeof vi.fn>).mockImplementation(() => undefined) | ||
|
|
| if (typeof plugin.generateBundle === 'function') { | ||
| await plugin.generateBundle.call( | ||
| {} as never, | ||
| {} as never, | ||
| {} as never, | ||
| false, | ||
| ) | ||
| } |
Defer build-time manifest writes to renderStart/generateBundle/buildEnd instead of sync-writing on every transform. Clarify flush tests around the real writer (via fs) and generateBundle's Rollup signature. Co-authored-by: Jeremy Butler <jrmybtlr@users.noreply.github.com>
In dual client+SSR setups, only the client instance writes the shared manifest so a smaller server class set cannot overwrite it. Classic build.ssr without an Environment API server consumer still flushes. Co-authored-by: Jeremy Butler <jrmybtlr@users.noreply.github.com>
|
@copilot resolve the merge conflicts in this pull request |
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
Merged |
Summary
manifestRootalready handles NuxtsrcDir, but there was no SSR-aware flush path. Builds could leave.classy/output.classy.htmlstale, and dual client+SSR environments could overwrite a shared manifest with a smaller server-side class set.Changes
flushManifest()and call it fromrenderStart,generateBundle, andbuildEndscheduleManifestWrite(flush hooks own the final write)consumer === 'server'/name === 'ssr') so only the client owns the shared filebuild.ssrwithout an Environment API server consumer still flushesconfig.build.ssrand the Environment API for logging / gatingWhy these hooks
build.ssr)renderStartflushgenerateBundleflushbuildEndflushReview follow-ups
Addressed Copilot review plus dual-env overwrite hardening:
fs(staticindeximport is not replaced byvi.doMock)generateBundle(outputOptions, bundle, isWrite)test callTest plan
renderStartflush