renderer: tree-shake unused rendu context helpers in production templates
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 11.2k
- Forks
- 899
- Avg merge
- 2d 24m
- Merged PRs (30d)
- 40
Description
Summary
The production renderer template (src/build/virtual/renderer-template.ts) compiles the HTML template when Nitro builds, then emits import { renderToResponse } from 'rendu'. renderToResponse always creates the full render context, so the output always includes rendu's cookie utilities (cookie-es parse/serialize plus the lazy $COOKIES proxy), even when the template never uses $COOKIES or setCookie.
Proposed change
rendu is adding a build-time codegen API, compileTemplateToModule(). It generates an ES module that imports and passes in only the render context variables the template actually uses. There is also a lower-level renderContextToResponse() with individually importable createRenderResponse / createRenderURL / createCookies / createSetCookie / createRedirect helpers.
This API is not released yet. The Nitro change is blocked until a rendu release includes it.
Bundling generated modules with rolldown against rendu's dist, with srvx left out:
| Case | Size | Cookie code |
|---|---|---|
Template using only {{ title }} and $URL |
~3.0 KB | no |
Template using setCookie and $COOKIES |
~9.2 KB | yes |
Current renderToResponse import |
~8.5 KB | always |
The production branch of renderer-template.ts would become roughly:
import { hasTemplateSyntax, compileTemplateToModule } from "rendu";
const isVite = nitro.options.builder === "vite";
const renderModule = compileTemplateToModule(html, {
contextKeys: ["fetch", "serverFetch", ...(isVite ? ["fetchViteEnv"] : [])],
});
return /* js */ `
${renderModule}
import { fetch, serverFetch } from 'nitro/app'
${isVite ? `import { fetchViteEnv } from "nitro/vite/runtime"` : ""}
const context = { fetch, serverFetch${isVite ? ", fetchViteEnv" : ""} }
export const rendererTemplate = (request) => render(request, context)
`;
The dev handler (renderer-template.dev.ts) can keep using compileTemplate + renderToResponse, since bundle size doesn't matter there.
Related bug (likely)
Today the production template is compiled with contextKeys: [...RENDER_CONTEXT_KEYS], but the context also contains fetch, serverFetch and (with Vite) fetchViteEnv. In contextKeys mode a compiled template can only see the listed names. So serverFetch / fetchViteEnv in a production template probably throw a ReferenceError, and fetch falls back to globalThis.fetch instead of Nitro's fetch. This is reasoned from the code and not reproduced in a running Nitro app. Dev works because it uses with() mode. Passing these names as contextKeys (as in the snippet above) fixes it.
Tasks
- Release rendu with
compileTemplateToModule - Switch the production renderer template to
compileTemplateToModule - Add a test for
serverFetch/fetchaccess in production renderer templates
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/build/virtual/renderer-template.ts and compare it with renderer-template.dev.ts, then confirm the required rendu release includes compileTemplateToModule(). Check the production renderer path for access to fetch, serverFetch, and fetchViteEnv. Done means production templates import only the context they use and a test covers serverFetch and fetch access.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend, build-system
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100