@webdiy/starter-vite-preset
v1.3.0
Published
WebDIY canonical Vite preset. Two layers: defineConfig wires @cloudflare/vite-plugin + preview-proxy-compat invariants (allowedHosts: true, hmr: false) + the WEBDIY_INJECT_KEYS managed-secret binding customizer; defineStaticConfig (./static) applies the i
Maintainers
Readme
@webdiy/starter-vite-preset
Canonical Vite preset for WebDIY starter templates. Two layers, one source of truth for the preview-proxy invariants.
What it does
The proxy-compat invariants (both layers enforce these):
- Sets
server.allowedHosts: true. Vite 5+ rejects unknown Host headers as a DNS-rebinding defense. The WebDIY preview proxy rewrites Host on its way to the dev server, so the dev server must accept any host. - Sets
server.hmr: false. The WebDIY preview proxy is HTTP-only and does not forward WebSocket upgrades. HMR errors out and pollutes the console; the workbench drives one deterministic reload per chat-turn instead.
These are requirements, not defaults. User config is merged in first, then server.allowedHosts and server.hmr are forcibly applied on top — passing server: { hmr: true } cannot override them.
The two layers:
defineConfig(root entry) — the full-stack layer: proxy-compat invariants plus@cloudflare/vite-pluginregistration (Workers-native dev + build; one bundle for client + server) plus the managed-secret binding customizer (below).defineStaticConfig(./staticentry) — proxy-compat invariants only, for templates that deploy as Workers Static Assets with no Worker code. The./staticmodule never imports@cloudflare/vite-plugin(the peer is marked optional), so static templates don't install the plugin.
Managed-secret bindings (WEBDIY_INJECT_KEYS, full-stack layer only):
The WebDIY platform delivers secret values to the dev server as spawn-time process
environment — never as a file — together with a WEBDIY_INJECT_KEYS variable naming
exactly which keys are platform-injected (comma-separated). The full-stack layer
registers a worker-config customizer that maps precisely those named process-env
values into the entry worker's vars bindings, so worker code reads them as env.X.
- Scoped by construction — only the named keys are mapped; the rest of the
process environment never enters the binding namespace (unlike
CLOUDFLARE_INCLUDE_PROCESS_ENV, which binds everything). - Inert outside the platform — with
WEBDIY_INJECT_KEYSunset (local dev, CI,vite build), the customizer adds nothing. - Rotation-ready — the set is re-read at every dev-server (re)start, which is when the platform delivers a fresh set.
- Precedence — an injected value wins over a same-named
[vars]entry from the wrangler config or a caller customizer (a managed secret is authoritative for its name).
Usage
Full-stack template (Worker code, CF plugin):
// vite.config.ts
import { defineConfig } from '@webdiy/starter-vite-preset';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
});Static-assets template (no Worker code):
// vite.config.ts
import { defineStaticConfig } from '@webdiy/starter-vite-preset/static';
import react from '@vitejs/plugin-react';
export default defineStaticConfig({
plugins: [react()],
});Both accept the same UserConfig shape as Vite's defineConfig. Anything you pass — plugins, resolve aliases, build config, environment, server.proxy — flows through unchanged. Only server.allowedHosts and server.hmr are non-overridable.
The full-stack defineConfig also takes an optional second argument for
plugin-level needs; a caller-supplied config customizer is composed with (and
runs before) the managed-secret customizer:
export default defineConfig(
{ plugins: [react()] },
{ cloudflare: { persistState: false } },
);When to use
- Full-stack Vite templates (deploy to Cloudflare Workers via
@cloudflare/vite-plugin, e.g.webdiy-template-fullstack) → rootdefineConfig. - Static-assets Vite templates (no Worker code, e.g.
webdiy-template-spa) →defineStaticConfigfrom./static. Do not hand-inline the server values — the preset is the single source of truth. - Skip the preset only for non-Vite frameworks (Astro, etc.) — Astro has its own
defineConfigfromastro/config; inline the proxy-compat values inastro.config.mjs's nestedviteblock.
Why a real npm package
- Single source of truth. Bumping a proxy-compat invariant or the CF plugin version is one package release, not N template SHA bumps.
- Self-disclosing. A WebDIY agent can
readFile node_modules/@webdiy/starter-vite-preset/README.mdto understand exactly what the preset enforces and why. No silent magic. - Disk-honest. No scaffolder mutators rewriting
vite.config.tsafter a template clone. The preset is just code the template imports.
Versioning
- Caret pin in templates (
"^1.0.0"). Bug fixes and additive minors pick up automatically. - Major bumps signal a proxy-compat invariant change (e.g. proxy gains WS support and
hmrbecomes optional). Templates must SHA-bump explicitly. 0.xis skipped — npm caret resolves only0.x.y → 0.x.*, defeating the auto-pickup. We start at1.0.0.
Peer dependencies
vite—^6.0.0 || ^7.0.0 || ^8.0.0@cloudflare/vite-plugin—^1.36.0(the entry-workerconfigcustomizer the managed-secret layer rides on landed in the 1.36 line)
vite is installed by every consuming template directly; the preset declares peers so
duplicates don't end up in the consumer's node_modules. @cloudflare/vite-plugin is an
optional peer (peerDependenciesMeta) — only templates using the root defineConfig
install it; ./static consumers (e.g. webdiy-template-spa) don't.
Build
This package lives inside the web-diy-starter-templates npm workspace. Run all commands from the workspace root.
# from web-diy-starter-templates/
npm install # installs all workspace packages, hoists deps
npm run build:preset # tsup → dist/{index.js,index.cjs,index.d.ts}Direct invocations (rare — prefer the workspace scripts):
npm --workspace=@webdiy/starter-vite-preset run build
npm --workspace=@webdiy/starter-vite-preset run typecheckPublish
Releases go through npm trusted publishing (OIDC) — no token, no OTP, provenance
generated automatically. The one-time trust is configured on npmjs.com (package
Settings → Trusted Publisher → GitHub Actions: owner webdotdiy, repository
web-diy-starter-templates, workflow release-preset.yml; done 2026-07-11).
# 1. bump the version + commit (from web-diy-starter-templates/)
npm --workspace=@webdiy/starter-vite-preset version <patch|minor|major>
git push origin main
# 2. publish from CI (OIDC — no credentials involved)
gh workflow run release-preset.yml -R webdotdiy/web-diy-starter-templatesThe workflow publishes whatever version package.json declares; prepublishOnly
runs the build + test suite, and npm refuses an already-published version, so
re-runs are safe. A local npm publish still works as a fallback but requires an
interactive 2FA OTP (bypass-2FA granular tokens are deprecated: management bypass
ends Aug 2026, direct publish ~Jan 2027 — trusted publishing is the durable path).
Why the version bump matters
Templates pin a caret range (currently "^1.2.0"). Caret picks up new minors and patches automatically — no template SHA bump needed. A major bump (e.g. 1.x → 2.0) requires every template to bump its caret pin and SHA-bump its repo, because the proxy-compat contract has changed.
The package's publishConfig.access: "public" in package.json ensures the scoped package is published publicly (npm scopes default to private otherwise).
