diff --git a/.changeset/chain-body-reads.md b/.changeset/chain-body-reads.md new file mode 100644 index 000000000000..fcf976f1d5d5 --- /dev/null +++ b/.changeset/chain-body-reads.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: keep at most one pending body read at a time when deserializing binary forms diff --git a/.changeset/config.json b/.changeset/config.json index 55fef6e0b141..6bd4a1b9e33d 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,7 +7,8 @@ "commit": false, "linked": [], "access": "public", - "baseBranch": "main", + "baseBranch": "version-3", "bumpVersionsWithWorkspaceProtocolOnly": true, - "ignore": ["!(@sveltejs/*)"] + "ignore": ["!(@sveltejs/*)"], + "format": false } diff --git a/.changeset/fix-client-manifest-vite-root.md b/.changeset/fix-client-manifest-vite-root.md new file mode 100644 index 000000000000..3255bb191953 --- /dev/null +++ b/.changeset/fix-client-manifest-vite-root.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: resolve client manifest imports against the Vite root diff --git a/.changeset/fuzzy-ducks-fix.md b/.changeset/fuzzy-ducks-fix.md new file mode 100644 index 000000000000..60532c6c7bf8 --- /dev/null +++ b/.changeset/fuzzy-ducks-fix.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: render the nearest `+error.svelte` at the depth it occupies when an error is thrown during rendering diff --git a/.changeset/kind-forms.md b/.changeset/kind-forms.md new file mode 100644 index 000000000000..1dcaede8fc23 --- /dev/null +++ b/.changeset/kind-forms.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: wait for the redirect navigation before remote form submissions resolve diff --git a/.changeset/lazy-pugs-refuse.md b/.changeset/lazy-pugs-refuse.md new file mode 100644 index 000000000000..7707628b8902 --- /dev/null +++ b/.changeset/lazy-pugs-refuse.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-netlify': patch +--- + +fix: strip trailing slashes from prerendered paths in the edge function exclude list, so the root page is served statically when using a base path diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 000000000000..d20efa9e7db8 --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,4 @@ +{ + "mode": "pre", + "tag": "next" +} diff --git a/.changeset/pre/adapter-node-manifest-mime-types.md b/.changeset/pre/adapter-node-manifest-mime-types.md new file mode 100644 index 000000000000..1d59ca72c56b --- /dev/null +++ b/.changeset/pre/adapter-node-manifest-mime-types.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-node': patch +--- + +fix: serve static files with the Content-Type recorded in the manifest diff --git a/.changeset/pre/afraid-peaches-stay.md b/.changeset/pre/afraid-peaches-stay.md new file mode 100644 index 000000000000..414bb33f131f --- /dev/null +++ b/.changeset/pre/afraid-peaches-stay.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": major +--- + +breaking: move remote function types to `$app/server` + \ No newline at end of file diff --git a/.changeset/pre/all-donkeys-rest.md b/.changeset/pre/all-donkeys-rest.md new file mode 100644 index 000000000000..ee89f1cc1561 --- /dev/null +++ b/.changeset/pre/all-donkeys-rest.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: persist global app state across module graph reloads diff --git a/.changeset/pre/allow-hyphenated-params-matchers.md b/.changeset/pre/allow-hyphenated-params-matchers.md new file mode 100644 index 000000000000..e1d4b387d5ca --- /dev/null +++ b/.changeset/pre/allow-hyphenated-params-matchers.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: allow hyphens in param and matcher names diff --git a/.changeset/pre/app-manifest-module.md b/.changeset/pre/app-manifest-module.md new file mode 100644 index 000000000000..0e115a368f83 --- /dev/null +++ b/.changeset/pre/app-manifest-module.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/kit": minor +--- + +feat: add `$app/manifest` module with `immutable`, `assets`, `prerendered`, and `routes` exports diff --git a/.changeset/pre/app-types-path-template.md b/.changeset/pre/app-types-path-template.md new file mode 100644 index 000000000000..99c9c25d49cb --- /dev/null +++ b/.changeset/pre/app-types-path-template.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: generate valid `Path` types for routes with optional or rest params, several params in one segment, or escape sequences diff --git a/.changeset/pre/beige-lights-taste.md b/.changeset/pre/beige-lights-taste.md new file mode 100644 index 000000000000..ee1b2dde81ee --- /dev/null +++ b/.changeset/pre/beige-lights-taste.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: ignore files with + prefix if they contain test/spec/stories diff --git a/.changeset/pre/better-clocks-own.md b/.changeset/pre/better-clocks-own.md new file mode 100644 index 000000000000..44d14bf69c5a --- /dev/null +++ b/.changeset/pre/better-clocks-own.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: remove `experimental.handleRenderingErrors` flag diff --git a/.changeset/pre/better-glasses-decide.md b/.changeset/pre/better-glasses-decide.md new file mode 100644 index 000000000000..413018ee119c --- /dev/null +++ b/.changeset/pre/better-glasses-decide.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-cloudflare': major +--- + +breaking: minimum Wrangler version required is `4.118.0` diff --git a/.changeset/pre/better-rats-care.md b/.changeset/pre/better-rats-care.md new file mode 100644 index 000000000000..1466c192fb1a --- /dev/null +++ b/.changeset/pre/better-rats-care.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: make `getRequest` and `setResponse` synchronous diff --git a/.changeset/pre/blue-plants-work.md b/.changeset/pre/blue-plants-work.md new file mode 100644 index 000000000000..51ffe066c255 --- /dev/null +++ b/.changeset/pre/blue-plants-work.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: TypeScript 6 is now the minimum required version diff --git a/.changeset/pre/bold-aliens-know.md b/.changeset/pre/bold-aliens-know.md new file mode 100644 index 000000000000..caf8ae728821 --- /dev/null +++ b/.changeset/pre/bold-aliens-know.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-vercel': patch +--- + +chore: bump `@vercel/nft` dependency diff --git a/.changeset/pre/brave-canyons-crash.md b/.changeset/pre/brave-canyons-crash.md new file mode 100644 index 000000000000..b64985e7ea20 --- /dev/null +++ b/.changeset/pre/brave-canyons-crash.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: add `ErrorProps` to generated types diff --git a/.changeset/pre/brave-files-cheat.md b/.changeset/pre/brave-files-cheat.md new file mode 100644 index 000000000000..a351ee978454 --- /dev/null +++ b/.changeset/pre/brave-files-cheat.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: support sourcemaps in production diff --git a/.changeset/pre/breezy-bananas-divide.md b/.changeset/pre/breezy-bananas-divide.md new file mode 100644 index 000000000000..086e36a3eba4 --- /dev/null +++ b/.changeset/pre/breezy-bananas-divide.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: refresh all load functions/queries when clicking a link to the current URL diff --git a/.changeset/pre/bright-mails-repair.md b/.changeset/pre/bright-mails-repair.md new file mode 100644 index 000000000000..f01879959d20 --- /dev/null +++ b/.changeset/pre/bright-mails-repair.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: allow `undefined` values to be passed to form field `.as(...)` where applicable diff --git a/.changeset/pre/bright-trees-listen.md b/.changeset/pre/bright-trees-listen.md new file mode 100644 index 000000000000..6fe760eb1023 --- /dev/null +++ b/.changeset/pre/bright-trees-listen.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/kit": major +--- + +breaking: move remote function types to `$app/server` diff --git a/.changeset/pre/brisk-otters-glow.md b/.changeset/pre/brisk-otters-glow.md new file mode 100644 index 000000000000..798b6f925f28 --- /dev/null +++ b/.changeset/pre/brisk-otters-glow.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-auto': patch +--- + +fix: convert resolved adapter path to a file URL before importing, so builds work on Windows diff --git a/.changeset/pre/brown-bears-retire-adapters.md b/.changeset/pre/brown-bears-retire-adapters.md new file mode 100644 index 000000000000..5b03c0f3415d --- /dev/null +++ b/.changeset/pre/brown-bears-retire-adapters.md @@ -0,0 +1,9 @@ +--- +'@sveltejs/adapter-node': patch +'@sveltejs/adapter-static': patch +'@sveltejs/adapter-cloudflare': patch +'@sveltejs/adapter-netlify': patch +'@sveltejs/adapter-vercel': patch +--- + +chore: use `node:fs` instead of deprecated `builder.rimraf` and `builder.mkdirp` diff --git a/.changeset/pre/brown-bears-retire.md b/.changeset/pre/brown-bears-retire.md new file mode 100644 index 000000000000..600c035e7b09 --- /dev/null +++ b/.changeset/pre/brown-bears-retire.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: deprecate `builder.rimraf` and `builder.mkdirp` in favour of `node:fs` methods diff --git a/.changeset/pre/callable-param-matchers.md b/.changeset/pre/callable-param-matchers.md new file mode 100644 index 000000000000..b6f6bf3107c7 --- /dev/null +++ b/.changeset/pre/callable-param-matchers.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: don't treat callable standard schemas as function param matchers diff --git a/.changeset/pre/calm-otters-package.md b/.changeset/pre/calm-otters-package.md new file mode 100644 index 000000000000..36d842d4cca8 --- /dev/null +++ b/.changeset/pre/calm-otters-package.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/package': patch +--- + +chore: remove dependency on sade diff --git a/.changeset/pre/calm-routes-smile.md b/.changeset/pre/calm-routes-smile.md new file mode 100644 index 000000000000..d032f79bc0e2 --- /dev/null +++ b/.changeset/pre/calm-routes-smile.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: generate route resolution modules as siblings of `.html` pages diff --git a/.changeset/pre/chilly-berries-press.md b/.changeset/pre/chilly-berries-press.md new file mode 100644 index 000000000000..f6f1dee5b098 --- /dev/null +++ b/.changeset/pre/chilly-berries-press.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: upgrade to `cookie` v2. Cookie names must now contain only ASCII characters diff --git a/.changeset/pre/chilly-ravens-kneel.md b/.changeset/pre/chilly-ravens-kneel.md new file mode 100644 index 000000000000..35cdd62003ae --- /dev/null +++ b/.changeset/pre/chilly-ravens-kneel.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-cloudflare': patch +--- + +chore: bump `@cloudflare/workers-types` to `4.20260621.1` diff --git a/.changeset/pre/clean-papers-give.md b/.changeset/pre/clean-papers-give.md new file mode 100644 index 000000000000..44d488dfee8f --- /dev/null +++ b/.changeset/pre/clean-papers-give.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/adapter-netlify': major +'@sveltejs/adapter-vercel': major +--- + +chore: use `rolldown` for edge function bundling diff --git a/.changeset/pre/cold-carrots-raise.md b/.changeset/pre/cold-carrots-raise.md new file mode 100644 index 000000000000..5b77b8c63f25 --- /dev/null +++ b/.changeset/pre/cold-carrots-raise.md @@ -0,0 +1,7 @@ +--- +'@sveltejs/package': major +'@sveltejs/kit': major +'@sveltejs/enhanced-img': major +--- + +breaking: require Node 22 or newer diff --git a/.changeset/pre/cold-pans-decode.md b/.changeset/pre/cold-pans-decode.md new file mode 100644 index 000000000000..0487f90c957f --- /dev/null +++ b/.changeset/pre/cold-pans-decode.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: decode all numeric character references, including above `ffff`, when crawling prerendered pages diff --git a/.changeset/pre/collect-nested-refreshes.md b/.changeset/pre/collect-nested-refreshes.md new file mode 100644 index 000000000000..c9a38622219d --- /dev/null +++ b/.changeset/pre/collect-nested-refreshes.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: include queries refreshed from within another query in the serialized response diff --git a/.changeset/pre/common-wings-doubt.md b/.changeset/pre/common-wings-doubt.md new file mode 100644 index 000000000000..594c9acb5d80 --- /dev/null +++ b/.changeset/pre/common-wings-doubt.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/kit": patch +--- + +fix: reject malformed streamed data encoding diff --git a/.changeset/pre/compress-returns-files.md b/.changeset/pre/compress-returns-files.md new file mode 100644 index 000000000000..469b582811bc --- /dev/null +++ b/.changeset/pre/compress-returns-files.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: return the list of compressed files from `builder.compress` diff --git a/.changeset/pre/cookies-get-cached-parse.md b/.changeset/pre/cookies-get-cached-parse.md new file mode 100644 index 000000000000..116660555683 --- /dev/null +++ b/.changeset/pre/cookies-get-cached-parse.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +perf: cache the default cookie header parse and avoid allocations in `cookies.get` diff --git a/.changeset/pre/cookies-getall-delete-phantom.md b/.changeset/pre/cookies-getall-delete-phantom.md new file mode 100644 index 000000000000..98ef96f66ac3 --- /dev/null +++ b/.changeset/pre/cookies-getall-delete-phantom.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: exclude deleted cookies from `cookies.getAll()` so it stays consistent with `cookies.get()` diff --git a/.changeset/pre/cozy-crabs-attend.md b/.changeset/pre/cozy-crabs-attend.md new file mode 100644 index 000000000000..586ac1fe300c --- /dev/null +++ b/.changeset/pre/cozy-crabs-attend.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: hide stack traces for internal errors like 404s diff --git a/.changeset/pre/crisp-adults-give.md b/.changeset/pre/crisp-adults-give.md new file mode 100644 index 000000000000..a89b0809bcfd --- /dev/null +++ b/.changeset/pre/crisp-adults-give.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/package": patch +--- + +chore: bump `svelte2tsx` to 0.7.59 + \ No newline at end of file diff --git a/.changeset/pre/crisp-steaks-jump.md b/.changeset/pre/crisp-steaks-jump.md new file mode 100644 index 000000000000..1607fdfa13b4 --- /dev/null +++ b/.changeset/pre/crisp-steaks-jump.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": minor +--- + +feat: better response logging + \ No newline at end of file diff --git a/.changeset/pre/csp-unsafe-inline-helper.md b/.changeset/pre/csp-unsafe-inline-helper.md new file mode 100644 index 000000000000..04bcb7c46f80 --- /dev/null +++ b/.changeset/pre/csp-unsafe-inline-helper.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: deduplicate repeated CSP directive handling diff --git a/.changeset/pre/cuddly-radios-brush.md b/.changeset/pre/cuddly-radios-brush.md new file mode 100644 index 000000000000..96091fec4f61 --- /dev/null +++ b/.changeset/pre/cuddly-radios-brush.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: remove the `preloadStrategy` option. `modulepreload` will always be used diff --git a/.changeset/pre/cuddly-tigers-attend.md b/.changeset/pre/cuddly-tigers-attend.md new file mode 100644 index 000000000000..2d1d9c4b5929 --- /dev/null +++ b/.changeset/pre/cuddly-tigers-attend.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: default the cookie `path` option to `'/'` diff --git a/.changeset/pre/curly-taxes-hug.md b/.changeset/pre/curly-taxes-hug.md new file mode 100644 index 000000000000..577e412b65cd --- /dev/null +++ b/.changeset/pre/curly-taxes-hug.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": patch +--- + +chore: bump `mrmime` to 2.0.1 + \ No newline at end of file diff --git a/.changeset/pre/curly-webs-lie.md b/.changeset/pre/curly-webs-lie.md new file mode 100644 index 000000000000..ffccd2bb4ad0 --- /dev/null +++ b/.changeset/pre/curly-webs-lie.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": patch +--- + +fix: correctly implement Vite plugin hook filters + \ No newline at end of file diff --git a/.changeset/pre/curvy-seas-camp.md b/.changeset/pre/curvy-seas-camp.md new file mode 100644 index 000000000000..a27ed7fa6153 --- /dev/null +++ b/.changeset/pre/curvy-seas-camp.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: warn when tsconfig doesn't exclude service worker diff --git a/.changeset/pre/cute-buses-camp.md b/.changeset/pre/cute-buses-camp.md new file mode 100644 index 000000000000..3ca0727cc833 --- /dev/null +++ b/.changeset/pre/cute-buses-camp.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/package': minor +--- + +feat: warn when using a `.server.` file or file inside a `server` directory without importing a server-only module diff --git a/.changeset/pre/cyan-zoos-love.md b/.changeset/pre/cyan-zoos-love.md new file mode 100644 index 000000000000..391ee6403465 --- /dev/null +++ b/.changeset/pre/cyan-zoos-love.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: remove `@sveltejs/kit/node/polyfills` diff --git a/.changeset/pre/deferred-query-refresh.md b/.changeset/pre/deferred-query-refresh.md new file mode 100644 index 000000000000..1c9ab7689f08 --- /dev/null +++ b/.changeset/pre/deferred-query-refresh.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: defer `query.refresh()` in server commands until after the command body completes diff --git a/.changeset/pre/drain-unconsumed-request-body.md b/.changeset/pre/drain-unconsumed-request-body.md new file mode 100644 index 000000000000..f115df9917a7 --- /dev/null +++ b/.changeset/pre/drain-unconsumed-request-body.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: drain unconsumed request bodies so keep-alive connections don't hang diff --git a/.changeset/pre/dry-experts-heal.md b/.changeset/pre/dry-experts-heal.md new file mode 100644 index 000000000000..bcd4ada0b0e9 --- /dev/null +++ b/.changeset/pre/dry-experts-heal.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: add `config.kit.output.linkHeaderPreload` to preload using the `Link` header diff --git a/.changeset/pre/dry-ties-scream.md b/.changeset/pre/dry-ties-scream.md new file mode 100644 index 000000000000..a3f924c1a867 --- /dev/null +++ b/.changeset/pre/dry-ties-scream.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: resolve service worker and `tsconfig.json` based on Vite `root` setting diff --git a/.changeset/pre/eager-bears-shake.md b/.changeset/pre/eager-bears-shake.md new file mode 100644 index 000000000000..531701a62b04 --- /dev/null +++ b/.changeset/pre/eager-bears-shake.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: rebuild the dev manifest when route files disappear during an incremental update diff --git a/.changeset/pre/eager-pans-wash.md b/.changeset/pre/eager-pans-wash.md new file mode 100644 index 000000000000..63df99f15c46 --- /dev/null +++ b/.changeset/pre/eager-pans-wash.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": patch +--- + +fix: only print prerender progress newline when necessary + \ No newline at end of file diff --git a/.changeset/pre/early-worlds-slide.md b/.changeset/pre/early-worlds-slide.md new file mode 100644 index 000000000000..f9280d7ee391 --- /dev/null +++ b/.changeset/pre/early-worlds-slide.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: require `@sveltejs/vite-plugin-svelte` v7 diff --git a/.changeset/pre/easy-items-smile.md b/.changeset/pre/easy-items-smile.md new file mode 100644 index 000000000000..e6b13aebd0bf --- /dev/null +++ b/.changeset/pre/easy-items-smile.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: validate that all remote form fields were created with form.fields.foo.as(...) diff --git a/.changeset/pre/easy-pianos-float.md b/.changeset/pre/easy-pianos-float.md new file mode 100644 index 000000000000..b755e97f0680 --- /dev/null +++ b/.changeset/pre/easy-pianos-float.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: make `page.url` immutable on a type level diff --git a/.changeset/pre/easy-shrimps-like.md b/.changeset/pre/easy-shrimps-like.md new file mode 100644 index 000000000000..07f74e1d4a1c --- /dev/null +++ b/.changeset/pre/easy-shrimps-like.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/enhanced-img': minor +--- + +breaking: require Vite 8 and `vite-plugin-svelte` 7 diff --git a/.changeset/pre/easy-weeks-tan.md b/.changeset/pre/easy-weeks-tan.md new file mode 100644 index 000000000000..8bfab1133b4b --- /dev/null +++ b/.changeset/pre/easy-weeks-tan.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: remove `base`, `assets`, and `resolveRoute` from `$app/paths` diff --git a/.changeset/pre/eight-lions-argue.md b/.changeset/pre/eight-lions-argue.md new file mode 100644 index 000000000000..90fb99a280f5 --- /dev/null +++ b/.changeset/pre/eight-lions-argue.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +perf: match only unpaired surrogates when escaping HTML diff --git a/.changeset/pre/eight-melons-brake.md b/.changeset/pre/eight-melons-brake.md new file mode 100644 index 000000000000..539608f5cd6c --- /dev/null +++ b/.changeset/pre/eight-melons-brake.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-netlify': patch +--- + +fix: ensure types for `platform.context` work diff --git a/.changeset/pre/eighty-cups-jam.md b/.changeset/pre/eighty-cups-jam.md new file mode 100644 index 000000000000..1b5aaa788248 --- /dev/null +++ b/.changeset/pre/eighty-cups-jam.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: treat `data:` protocol URLs as external for redirect diff --git a/.changeset/pre/eighty-donuts-argue.md b/.changeset/pre/eighty-donuts-argue.md new file mode 100644 index 000000000000..bd7ce1305678 --- /dev/null +++ b/.changeset/pre/eighty-donuts-argue.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: deduplicate request hashing for serialized fetch responses diff --git a/.changeset/pre/eighty-paws-love.md b/.changeset/pre/eighty-paws-love.md new file mode 100644 index 000000000000..b301fa8f1b2c --- /dev/null +++ b/.changeset/pre/eighty-paws-love.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: remove `createEntries` from the `Builder` object passed to adapter functions diff --git a/.changeset/pre/eleven-cases-guard.md b/.changeset/pre/eleven-cases-guard.md new file mode 100644 index 000000000000..0b070264ee9a --- /dev/null +++ b/.changeset/pre/eleven-cases-guard.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: don't treat `Object.prototype` members as param matchers during validation diff --git a/.changeset/pre/elliott-is-uncreative.md b/.changeset/pre/elliott-is-uncreative.md new file mode 100644 index 000000000000..454548f4f068 --- /dev/null +++ b/.changeset/pre/elliott-is-uncreative.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: return no content for 204 responses diff --git a/.changeset/pre/env-empty-string.md b/.changeset/pre/env-empty-string.md new file mode 100644 index 000000000000..e90321326601 --- /dev/null +++ b/.changeset/pre/env-empty-string.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: don't report empty environment variables as missing diff --git a/.changeset/pre/env-function-validators.md b/.changeset/pre/env-function-validators.md new file mode 100644 index 000000000000..09c0d00fc946 --- /dev/null +++ b/.changeset/pre/env-function-validators.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: support function validators for environment variables diff --git a/.changeset/pre/externalize-opentelemetry-api.md b/.changeset/pre/externalize-opentelemetry-api.md new file mode 100644 index 000000000000..2c1f00dc698f --- /dev/null +++ b/.changeset/pre/externalize-opentelemetry-api.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/kit': patch +'@sveltejs/adapter-node': patch +--- + +fix: externalize `@opentelemetry/api` to prevent bundler chunk colocation between `instrumentation.server.js` and application code diff --git a/.changeset/pre/fair-baths-pull.md b/.changeset/pre/fair-baths-pull.md new file mode 100644 index 000000000000..ceb8ee65e2d7 --- /dev/null +++ b/.changeset/pre/fair-baths-pull.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-netlify': patch +--- + +fix: include `utils.js` in package.json `files` diff --git a/.changeset/pre/famous-bars-search.md b/.changeset/pre/famous-bars-search.md new file mode 100644 index 000000000000..3e1e8b8af2ab --- /dev/null +++ b/.changeset/pre/famous-bars-search.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/kit': major +--- + +breaking: rename `Pathname` type to `Path` and `Asset` to `AssetPath` +breaking: remove leading `/` from `Path` and `AssetPath` diff --git a/.changeset/pre/famous-pandas-hammer.md b/.changeset/pre/famous-pandas-hammer.md new file mode 100644 index 000000000000..0bcfccf2635c --- /dev/null +++ b/.changeset/pre/famous-pandas-hammer.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: exclude routes without a page or endpoint from `routes` in `$app/manifest`, and remove directories with no route files from `LayoutParams` diff --git a/.changeset/pre/famous-yaks-kneel.md b/.changeset/pre/famous-yaks-kneel.md new file mode 100644 index 000000000000..c86154ee00e6 --- /dev/null +++ b/.changeset/pre/famous-yaks-kneel.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: `handle`'s `resolve` is now typed to always return a `Promise` diff --git a/.changeset/pre/fast-feet-travel.md b/.changeset/pre/fast-feet-travel.md new file mode 100644 index 000000000000..9203f202904c --- /dev/null +++ b/.changeset/pre/fast-feet-travel.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: avoid Vite dev server reload on initial page request diff --git a/.changeset/pre/feat-service-worker-module.md b/.changeset/pre/feat-service-worker-module.md new file mode 100644 index 000000000000..64f935119496 --- /dev/null +++ b/.changeset/pre/feat-service-worker-module.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/kit": minor +--- + +feat: use `type: 'module'` for service worker registrations diff --git a/.changeset/pre/fetch-accept-language.md b/.changeset/pre/fetch-accept-language.md new file mode 100644 index 000000000000..0594126fd625 --- /dev/null +++ b/.changeset/pre/fetch-accept-language.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: don't set a `null` `accept-language` header on internal `fetch` sub-requests when the incoming request has none diff --git a/.changeset/pre/fetch-prerendered-base.md b/.changeset/pre/fetch-prerendered-base.md new file mode 100644 index 000000000000..87e2fa759ec8 --- /dev/null +++ b/.changeset/pre/fetch-prerendered-base.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: correctly detect prerendered paths in server `fetch` when `paths.base` is set diff --git a/.changeset/pre/fine-ghosts-camp.md b/.changeset/pre/fine-ghosts-camp.md new file mode 100644 index 000000000000..6eacab83c095 --- /dev/null +++ b/.changeset/pre/fine-ghosts-camp.md @@ -0,0 +1,7 @@ +--- +'@sveltejs/adapter-netlify': major +--- + +breaking: write output that conforms to the stable [Netlify Frameworks API](https://docs.netlify.com/build/frameworks/frameworks-api/). + +Deploying and previewing with Netlify CLI now requires [v17.31.0](https://github.com/netlify/cli/releases/tag/v17.31.0) or later. Run `npm i -g netlify-cli@latest` to upgrade. diff --git a/.changeset/pre/five-readers-march.md b/.changeset/pre/five-readers-march.md new file mode 100644 index 000000000000..e9fb4707d606 --- /dev/null +++ b/.changeset/pre/five-readers-march.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-cloudflare': major +--- + +breaking: upgrade `@cloudflare/workers-types` to 4.20260219.0 diff --git a/.changeset/pre/fix-app-env-vite-node.md b/.changeset/pre/fix-app-env-vite-node.md new file mode 100644 index 000000000000..0bc33ba2c195 --- /dev/null +++ b/.changeset/pre/fix-app-env-vite-node.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: populate `$app/env/*` dynamic variables in contexts that don't run the dev server, such as `vite-node` diff --git a/.changeset/pre/fix-date-form-fields-set.md b/.changeset/pre/fix-date-form-fields-set.md new file mode 100644 index 000000000000..b9715242d4f4 --- /dev/null +++ b/.changeset/pre/fix-date-form-fields-set.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/kit": patch +--- + +fix: properly handle Date objects in form.fields.set diff --git a/.changeset/pre/fix-form-action-prototype-pollution.md b/.changeset/pre/fix-form-action-prototype-pollution.md new file mode 100644 index 000000000000..788903de55cc --- /dev/null +++ b/.changeset/pre/fix-form-action-prototype-pollution.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: return 404 for form actions and remote functions whose name is an `Object.prototype` member diff --git a/.changeset/pre/fix-impossible-situation-server-only.md b/.changeset/pre/fix-impossible-situation-server-only.md new file mode 100644 index 000000000000..c083bfea84a5 --- /dev/null +++ b/.changeset/pre/fix-impossible-situation-server-only.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: no longer throw "An impossible situation occurred" when a server-only module is imported by both server and client code diff --git a/.changeset/pre/fix-vite-verbose.md b/.changeset/pre/fix-vite-verbose.md new file mode 100644 index 000000000000..7c0bf1da8889 --- /dev/null +++ b/.changeset/pre/fix-vite-verbose.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: respect Vite default log level diff --git a/.changeset/pre/fluffy-clouds-sing.md b/.changeset/pre/fluffy-clouds-sing.md new file mode 100644 index 000000000000..747412a45b38 --- /dev/null +++ b/.changeset/pre/fluffy-clouds-sing.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/enhanced-img': patch +--- + +chore: replace the `$lib` alias with `#lib` in docs diff --git a/.changeset/pre/fluffy-dolls-grow.md b/.changeset/pre/fluffy-dolls-grow.md new file mode 100644 index 000000000000..9974b9f90f03 --- /dev/null +++ b/.changeset/pre/fluffy-dolls-grow.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: require Svelte config options to be passed through the Vite plugin diff --git a/.changeset/pre/fluffy-tires-judge.md b/.changeset/pre/fluffy-tires-judge.md new file mode 100644 index 000000000000..f5a5e090b2f9 --- /dev/null +++ b/.changeset/pre/fluffy-tires-judge.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-cloudflare': major +--- + +breaking: upgrade minimum `wrangler` version to ^4.67.0 diff --git a/.changeset/pre/form-action-http-status-codes.md b/.changeset/pre/form-action-http-status-codes.md new file mode 100644 index 000000000000..f4aaabe17a0f --- /dev/null +++ b/.changeset/pre/form-action-http-status-codes.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: form action responses now use the HTTP status code returned from `fail` diff --git a/.changeset/pre/forty-ducks-fly.md b/.changeset/pre/forty-ducks-fly.md new file mode 100644 index 000000000000..98c98783ee29 --- /dev/null +++ b/.changeset/pre/forty-ducks-fly.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-cloudflare': major +--- + +breaking: remove `platform.context` in favour of `platform.ctx` diff --git a/.changeset/pre/forty-owls-move.md b/.changeset/pre/forty-owls-move.md new file mode 100644 index 000000000000..74f18113f9f8 --- /dev/null +++ b/.changeset/pre/forty-owls-move.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/adapter-netlify": patch +--- + +chore: replace @netlify/functions with @netlify/types for the Context type diff --git a/.changeset/pre/fresh-dodos-share.md b/.changeset/pre/fresh-dodos-share.md new file mode 100644 index 000000000000..637c73718c24 --- /dev/null +++ b/.changeset/pre/fresh-dodos-share.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: don't duplicate remote modules in the generated manifest diff --git a/.changeset/pre/fresh-pots-smoke.md b/.changeset/pre/fresh-pots-smoke.md new file mode 100644 index 000000000000..7ebeba8835bf --- /dev/null +++ b/.changeset/pre/fresh-pots-smoke.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: write tsconfig to `node_modules/$app/tsconfig` diff --git a/.changeset/pre/fresh-shallows-navigate.md b/.changeset/pre/fresh-shallows-navigate.md new file mode 100644 index 000000000000..10c57b644436 --- /dev/null +++ b/.changeset/pre/fresh-shallows-navigate.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: add shallow routing to `goto` and deprecate `pushState` and `replaceState` diff --git a/.changeset/pre/fresh-shoes-count.md b/.changeset/pre/fresh-shoes-count.md new file mode 100644 index 000000000000..b87a98b31a48 --- /dev/null +++ b/.changeset/pre/fresh-shoes-count.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: reuse base64 and text decoding helpers diff --git a/.changeset/pre/friendly-removed-modules.md b/.changeset/pre/friendly-removed-modules.md new file mode 100644 index 000000000000..51e5a4f24698 --- /dev/null +++ b/.changeset/pre/friendly-removed-modules.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: explain the removal of `$lib` and `$service-worker` when their imports fail to resolve diff --git a/.changeset/pre/full-turtles-tell.md b/.changeset/pre/full-turtles-tell.md new file mode 100644 index 000000000000..0a08847d9f61 --- /dev/null +++ b/.changeset/pre/full-turtles-tell.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: correctly massage stack traces with async frames diff --git a/.changeset/pre/funky-turkeys-serve.md b/.changeset/pre/funky-turkeys-serve.md new file mode 100644 index 000000000000..e422cb293019 --- /dev/null +++ b/.changeset/pre/funky-turkeys-serve.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: mark `RequestEvent` properties as `readonly` diff --git a/.changeset/pre/gentle-impalas-heal.md b/.changeset/pre/gentle-impalas-heal.md new file mode 100644 index 000000000000..89a42162dc97 --- /dev/null +++ b/.changeset/pre/gentle-impalas-heal.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: move `defineParams` and associated types to `@sveltejs/kit/params` diff --git a/.changeset/pre/gentle-radios-go.md b/.changeset/pre/gentle-radios-go.md new file mode 100644 index 000000000000..33d159e8acb5 --- /dev/null +++ b/.changeset/pre/gentle-radios-go.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: remove the deprecated CSRF `checkOrigin` option in favor of `trustedOrigins` diff --git a/.changeset/pre/gentle-signs-cross.md b/.changeset/pre/gentle-signs-cross.md new file mode 100644 index 000000000000..b819bf018351 --- /dev/null +++ b/.changeset/pre/gentle-signs-cross.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: the `delta` property now only exists for `popstate` navigation events diff --git a/.changeset/pre/gentle-toys-talk.md b/.changeset/pre/gentle-toys-talk.md new file mode 100644 index 000000000000..cb9b7082229b --- /dev/null +++ b/.changeset/pre/gentle-toys-talk.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: change `form.error` type from `any` to `App.Error | undefined` diff --git a/.changeset/pre/get-request-response-close.md b/.changeset/pre/get-request-response-close.md new file mode 100644 index 000000000000..318e3c8aa801 --- /dev/null +++ b/.changeset/pre/get-request-response-close.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/kit': minor +'@sveltejs/adapter-node': patch +--- + +feat: abort `request.signal` when the response closes prematurely, via a new `response` option for `getRequest` diff --git a/.changeset/pre/giant-numbers-care.md b/.changeset/pre/giant-numbers-care.md new file mode 100644 index 000000000000..e033cd7e2d28 --- /dev/null +++ b/.changeset/pre/giant-numbers-care.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: remove deprecated `pragma` header in version polling for improved CORS support diff --git a/.changeset/pre/gold-tips-lead.md b/.changeset/pre/gold-tips-lead.md new file mode 100644 index 000000000000..a8fd52037ada --- /dev/null +++ b/.changeset/pre/gold-tips-lead.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": patch +--- + +chore: bump `@sveltejs/acorn-typescript` to 1.0.12 + \ No newline at end of file diff --git a/.changeset/pre/goto-errors-external-route.md b/.changeset/pre/goto-errors-external-route.md new file mode 100644 index 000000000000..78955c4e1130 --- /dev/null +++ b/.changeset/pre/goto-errors-external-route.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: `goto` now rejects when called with a URL that does not resolve to a route within the app, matching the existing behaviour for external URLs \ No newline at end of file diff --git a/.changeset/pre/great-ears-thank.md b/.changeset/pre/great-ears-thank.md new file mode 100644 index 000000000000..8152b898a22e --- /dev/null +++ b/.changeset/pre/great-ears-thank.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: update `match` parameter type diff --git a/.changeset/pre/green-globs-shift.md b/.changeset/pre/green-globs-shift.md new file mode 100644 index 000000000000..5dd3b1d482d6 --- /dev/null +++ b/.changeset/pre/green-globs-shift.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/package': patch +--- + +fix: rewrite `.ts` extensions in `import.meta.glob` patterns when `rewriteRelativeImportExtensions` is enabled diff --git a/.changeset/pre/grumpy-sites-float.md b/.changeset/pre/grumpy-sites-float.md new file mode 100644 index 000000000000..55d6ebc378a8 --- /dev/null +++ b/.changeset/pre/grumpy-sites-float.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-static': patch +--- + +fix: avoid caching immutable asset 404s when configuring for Vercel diff --git a/.changeset/pre/handle-error-all-errors.md b/.changeset/pre/handle-error-all-errors.md new file mode 100644 index 000000000000..f40f914478e3 --- /dev/null +++ b/.changeset/pre/handle-error-all-errors.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: run all errors through the `handleError` hook diff --git a/.changeset/pre/happy-coins-think.md b/.changeset/pre/happy-coins-think.md new file mode 100644 index 000000000000..76fdac4f7088 --- /dev/null +++ b/.changeset/pre/happy-coins-think.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/package': minor +--- + +feat: transform import aliases into relative imports in files diff --git a/.changeset/pre/happy-deer-drive.md b/.changeset/pre/happy-deer-drive.md new file mode 100644 index 000000000000..945d4b46a4e1 --- /dev/null +++ b/.changeset/pre/happy-deer-drive.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/kit": patch +--- + +fix: generate sourcemaps for remote modules diff --git a/.changeset/pre/heavy-showers-leave.md b/.changeset/pre/heavy-showers-leave.md new file mode 100644 index 000000000000..4cc474f845a0 --- /dev/null +++ b/.changeset/pre/heavy-showers-leave.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: require Svelte 5.56.4 or newer diff --git a/.changeset/pre/hide-query-event-url.md b/.changeset/pre/hide-query-event-url.md new file mode 100644 index 000000000000..4c4af4a22c65 --- /dev/null +++ b/.changeset/pre/hide-query-event-url.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: error on `event.url`, `event.params` and `event.route` access inside queries diff --git a/.changeset/pre/hip-apes-marry.md b/.changeset/pre/hip-apes-marry.md new file mode 100644 index 000000000000..f41133600485 --- /dev/null +++ b/.changeset/pre/hip-apes-marry.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-cloudflare': patch +--- + +fix: avoid overriding user's existing `_headers` rules diff --git a/.changeset/pre/hip-lions-check.md b/.changeset/pre/hip-lions-check.md new file mode 100644 index 000000000000..3723cc6dd598 --- /dev/null +++ b/.changeset/pre/hip-lions-check.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: avoid empty getElementById() call on hash routing navigation diff --git a/.changeset/pre/honest-pumas-leave.md b/.changeset/pre/honest-pumas-leave.md new file mode 100644 index 000000000000..c3e7c1d7ec27 --- /dev/null +++ b/.changeset/pre/honest-pumas-leave.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: make `$app/paths` importable in service workers diff --git a/.changeset/pre/hungry-jokes-repeat.md b/.changeset/pre/hungry-jokes-repeat.md new file mode 100644 index 000000000000..b29a7a10f6e8 --- /dev/null +++ b/.changeset/pre/hungry-jokes-repeat.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: enhanced cross-page form actions now navigate to the action page on success and failure, matching native form behavior diff --git a/.changeset/pre/ico-mime-type.md b/.changeset/pre/ico-mime-type.md new file mode 100644 index 000000000000..4d0923ee7581 --- /dev/null +++ b/.changeset/pre/ico-mime-type.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: serve `.ico` files with `image/x-icon` Content-Type diff --git a/.changeset/pre/icy-turtles-leave.md b/.changeset/pre/icy-turtles-leave.md new file mode 100644 index 000000000000..6634ac126978 --- /dev/null +++ b/.changeset/pre/icy-turtles-leave.md @@ -0,0 +1,7 @@ +--- +"@sveltejs/enhanced-img": patch +"@sveltejs/kit": patch +--- + +chore: bump `magic-string` to 1.1.0 + \ No newline at end of file diff --git a/.changeset/pre/inline-page-manifest.md b/.changeset/pre/inline-page-manifest.md new file mode 100644 index 000000000000..4840de26320a --- /dev/null +++ b/.changeset/pre/inline-page-manifest.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: exclude inlined files from the page's `$app/manifest` immutable list diff --git a/.changeset/pre/jolly-adults-wave.md b/.changeset/pre/jolly-adults-wave.md new file mode 100644 index 000000000000..8768b91212ba --- /dev/null +++ b/.changeset/pre/jolly-adults-wave.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: avoid client-side code being bundled by Cloudflare Wrangler diff --git a/.changeset/pre/khaki-lights-design.md b/.changeset/pre/khaki-lights-design.md new file mode 100644 index 000000000000..d406cd9ef060 --- /dev/null +++ b/.changeset/pre/khaki-lights-design.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: delete `$service-worker` module diff --git a/.changeset/pre/kit-prerendered-mime-types.md b/.changeset/pre/kit-prerendered-mime-types.md new file mode 100644 index 000000000000..c368d781dcb8 --- /dev/null +++ b/.changeset/pre/kit-prerendered-mime-types.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: record the mime types of prerendered paths in the server manifest diff --git a/.changeset/pre/large-onions-attack.md b/.changeset/pre/large-onions-attack.md new file mode 100644 index 000000000000..aef0217173b9 --- /dev/null +++ b/.changeset/pre/large-onions-attack.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-node': major +--- + +chore: migrate from rollup to rolldown diff --git a/.changeset/pre/large-pugs-clap.md b/.changeset/pre/large-pugs-clap.md new file mode 100644 index 000000000000..1bf95a91ef76 --- /dev/null +++ b/.changeset/pre/large-pugs-clap.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-static': patch +--- + +fix: match adapter-vercel's prerendered redirect handling when deploying to Vercel diff --git a/.changeset/pre/late-lands-watch.md b/.changeset/pre/late-lands-watch.md new file mode 100644 index 000000000000..dee8903df837 --- /dev/null +++ b/.changeset/pre/late-lands-watch.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: preserve page state set through `goto(..., { state, persistState: true })` across reloads diff --git a/.changeset/pre/late-lions-crash.md b/.changeset/pre/late-lions-crash.md new file mode 100644 index 000000000000..f120c80d8caa --- /dev/null +++ b/.changeset/pre/late-lions-crash.md @@ -0,0 +1,9 @@ +--- +"@sveltejs/adapter-auto": major +"@sveltejs/adapter-cloudflare": major +"@sveltejs/adapter-netlify": major +"@sveltejs/adapter-node": major +"@sveltejs/adapter-static": major +--- + +breaking: require SvelteKit 3 diff --git a/.changeset/pre/lazy-glasses-love.md b/.changeset/pre/lazy-glasses-love.md new file mode 100644 index 000000000000..89570d79d0ad --- /dev/null +++ b/.changeset/pre/lazy-glasses-love.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: move `defineEnvVars` to `@sveltejs/kit/env` diff --git a/.changeset/pre/lazy-nights-lay.md b/.changeset/pre/lazy-nights-lay.md new file mode 100644 index 000000000000..d90b6229d358 --- /dev/null +++ b/.changeset/pre/lazy-nights-lay.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: nested server-only directories diff --git a/.changeset/pre/lazy-tools-bathe.md b/.changeset/pre/lazy-tools-bathe.md new file mode 100644 index 000000000000..f0fe3d2d267d --- /dev/null +++ b/.changeset/pre/lazy-tools-bathe.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +feat: allow adapters to provide additional Vite plugins diff --git a/.changeset/pre/legacy-env-imports.md b/.changeset/pre/legacy-env-imports.md new file mode 100644 index 000000000000..4ca3ffec668b --- /dev/null +++ b/.changeset/pre/legacy-env-imports.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: reinstate `$env/static/private`, `$env/dynamic/private`, `$env/static/public`, `$env/dynamic/public` and `$app/environment` as deprecated aliases for `$app/env/private` `$app/env/public` and `$app/env` diff --git a/.changeset/pre/lemon-poems-refuse.md b/.changeset/pre/lemon-poems-refuse.md new file mode 100644 index 000000000000..72ce047cadd3 --- /dev/null +++ b/.changeset/pre/lemon-poems-refuse.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: clarify which hooks run during server route resolution diff --git a/.changeset/pre/lemon-pots-tease.md b/.changeset/pre/lemon-pots-tease.md new file mode 100644 index 000000000000..d8945ba267a3 --- /dev/null +++ b/.changeset/pre/lemon-pots-tease.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": patch +--- + +fix: yield to allow prerender updates to be visible + \ No newline at end of file diff --git a/.changeset/pre/lemon-turkeys-drop.md b/.changeset/pre/lemon-turkeys-drop.md new file mode 100644 index 000000000000..12244229414d --- /dev/null +++ b/.changeset/pre/lemon-turkeys-drop.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/kit": patch +--- + +fix: make `paths.origin` type looser diff --git a/.changeset/pre/lemon-ways-glow.md b/.changeset/pre/lemon-ways-glow.md new file mode 100644 index 000000000000..9e61ce1ace12 --- /dev/null +++ b/.changeset/pre/lemon-ways-glow.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": patch +--- + +chore: bump `devalue` to 5.9.0 + \ No newline at end of file diff --git a/.changeset/pre/lib-alias-to-hash-lib.md b/.changeset/pre/lib-alias-to-hash-lib.md new file mode 100644 index 000000000000..63922d566c56 --- /dev/null +++ b/.changeset/pre/lib-alias-to-hash-lib.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: replace the `$lib` alias with `#lib` and remove `files.lib` config. diff --git a/.changeset/pre/light-apples-sleep.md b/.changeset/pre/light-apples-sleep.md new file mode 100644 index 000000000000..bf9ff2429491 --- /dev/null +++ b/.changeset/pre/light-apples-sleep.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": major +--- + +breaking: remove `#lib` definition from `paths`; requires explicit module extensions as a result + \ No newline at end of file diff --git a/.changeset/pre/light-singers-lie.md b/.changeset/pre/light-singers-lie.md new file mode 100644 index 000000000000..745edec0deb3 --- /dev/null +++ b/.changeset/pre/light-singers-lie.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +chore: change `error`, `isHttpError`, `redirect`, and `isRedirect` to refer to public type instead of internal class diff --git a/.changeset/pre/live-query-keep-alive.md b/.changeset/pre/live-query-keep-alive.md new file mode 100644 index 000000000000..fefa78a6cd92 --- /dev/null +++ b/.changeset/pre/live-query-keep-alive.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +feat: send periodic `keep-alive` SSE comments from `query.live` to prevent idle-timeout errors diff --git a/.changeset/pre/loose-cases-know.md b/.changeset/pre/loose-cases-know.md new file mode 100644 index 000000000000..2a95d4074b7e --- /dev/null +++ b/.changeset/pre/loose-cases-know.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": major +--- + +breaking: move hooks-related types to `@sveltejs/kit/hooks` + \ No newline at end of file diff --git a/.changeset/pre/loose-pants-notice.md b/.changeset/pre/loose-pants-notice.md new file mode 100644 index 000000000000..976030f41074 --- /dev/null +++ b/.changeset/pre/loose-pants-notice.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: warn if hook files are spelled as "hook" instead of "hooks" diff --git a/.changeset/pre/lucky-moons-resolve.md b/.changeset/pre/lucky-moons-resolve.md new file mode 100644 index 000000000000..83155bcaa073 --- /dev/null +++ b/.changeset/pre/lucky-moons-resolve.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: only generate each route's resolution module once when prerendering diff --git a/.changeset/pre/manifest-route-capabilities.md b/.changeset/pre/manifest-route-capabilities.md new file mode 100644 index 000000000000..e60555b02fcc --- /dev/null +++ b/.changeset/pre/manifest-route-capabilities.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: add `page` and `endpoint` booleans to `$app/manifest`'s `routes`, and export a `ManifestRoute` type diff --git a/.changeset/pre/metal-bats-care.md b/.changeset/pre/metal-bats-care.md new file mode 100644 index 000000000000..d1cddcaa0dfe --- /dev/null +++ b/.changeset/pre/metal-bats-care.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-netlify': patch +--- + +fix: provide distinct names for each split serverless function diff --git a/.changeset/pre/modern-dodos-hang.md b/.changeset/pre/modern-dodos-hang.md new file mode 100644 index 000000000000..8ec45c840c9a --- /dev/null +++ b/.changeset/pre/modern-dodos-hang.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: add 'error' result type to `preloadData` diff --git a/.changeset/pre/moody-bags-heal.md b/.changeset/pre/moody-bags-heal.md new file mode 100644 index 000000000000..1b12a3b4509d --- /dev/null +++ b/.changeset/pre/moody-bags-heal.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: `$app/service-worker` module diff --git a/.changeset/pre/more-frequent-updated-checks.md b/.changeset/pre/more-frequent-updated-checks.md new file mode 100644 index 000000000000..9193a86af27d --- /dev/null +++ b/.changeset/pre/more-frequent-updated-checks.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: detect new deployments on data, remote, and form action responses, tab focus, and visibility change, and default `version.pollInterval` to 1 hour diff --git a/.changeset/pre/nasty-impalas-wear.md b/.changeset/pre/nasty-impalas-wear.md new file mode 100644 index 000000000000..14fef49885f0 --- /dev/null +++ b/.changeset/pre/nasty-impalas-wear.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: resolve paths using the Vite config `root` option instead of `process.cwd()` to better support monorepo configurations such as Vitest workspaces diff --git a/.changeset/pre/neat-gifts-feel.md b/.changeset/pre/neat-gifts-feel.md new file mode 100644 index 000000000000..eb05df95d0d7 --- /dev/null +++ b/.changeset/pre/neat-gifts-feel.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: disallow cross-origin form submissions without a `Content-Type` header diff --git a/.changeset/pre/neat-llamas-guess.md b/.changeset/pre/neat-llamas-guess.md new file mode 100644 index 000000000000..1916fee7ba48 --- /dev/null +++ b/.changeset/pre/neat-llamas-guess.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: Server-only directories (`/server/` in the path) are now treated as server-only everywhere inside the project (except `src/routes` and the assets directory) diff --git a/.changeset/pre/neat-showers-allow.md b/.changeset/pre/neat-showers-allow.md new file mode 100644 index 000000000000..dc52696c98aa --- /dev/null +++ b/.changeset/pre/neat-showers-allow.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/adapter-node': minor +'@sveltejs/kit': minor +--- + +feat: better error logging diff --git a/.changeset/pre/nice-mammals-move.md b/.changeset/pre/nice-mammals-move.md new file mode 100644 index 000000000000..e58b67ee2fcd --- /dev/null +++ b/.changeset/pre/nice-mammals-move.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: `config` exported from a universal route file takes precedence over a server one diff --git a/.changeset/pre/nine-coins-cheer.md b/.changeset/pre/nine-coins-cheer.md new file mode 100644 index 000000000000..6880deb22899 --- /dev/null +++ b/.changeset/pre/nine-coins-cheer.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: require Vite 8. Provides new functionality even for existing Vite 8 users such as faster builds with Vite hook filters and more powerful SvelteKit adapters with the Vite environment API diff --git a/.changeset/pre/nine-suns-nail.md b/.changeset/pre/nine-suns-nail.md new file mode 100644 index 000000000000..9481dfd70d9f --- /dev/null +++ b/.changeset/pre/nine-suns-nail.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: remove `data-sveltekit-*` option `'off'` in favour of `false` diff --git a/.changeset/pre/o11y-leave-experimental.md b/.changeset/pre/o11y-leave-experimental.md new file mode 100644 index 000000000000..c25b33b097af --- /dev/null +++ b/.changeset/pre/o11y-leave-experimental.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: move tracing out of the experimental namespace and remove the instrumentation flag diff --git a/.changeset/pre/odd-seas-refuse.md b/.changeset/pre/odd-seas-refuse.md new file mode 100644 index 000000000000..00300ec4b44b --- /dev/null +++ b/.changeset/pre/odd-seas-refuse.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: skip clean fields when programmatically validating forms diff --git a/.changeset/pre/olive-boxes-tan.md b/.changeset/pre/olive-boxes-tan.md new file mode 100644 index 000000000000..f2eda707e12b --- /dev/null +++ b/.changeset/pre/olive-boxes-tan.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +perf: skip import graph collection outside client environments diff --git a/.changeset/pre/olive-boxes-walk.md b/.changeset/pre/olive-boxes-walk.md new file mode 100644 index 000000000000..8480b853aa87 --- /dev/null +++ b/.changeset/pre/olive-boxes-walk.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +breaking: experimental remote form `validate({ includeUntouched })` option is now `all` diff --git a/.changeset/pre/open-wasps-shop.md b/.changeset/pre/open-wasps-shop.md new file mode 100644 index 000000000000..366573abe6fc --- /dev/null +++ b/.changeset/pre/open-wasps-shop.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: avoid client build warning about externalising `node:async_hooks` diff --git a/.changeset/pre/package-fs-helpers.md b/.changeset/pre/package-fs-helpers.md new file mode 100644 index 000000000000..5b2e03cd03c1 --- /dev/null +++ b/.changeset/pre/package-fs-helpers.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/package': patch +--- + +chore: replace private fs helpers with Node built-ins diff --git a/.changeset/pre/page-endpoint-route-ids.md b/.changeset/pre/page-endpoint-route-ids.md new file mode 100644 index 000000000000..dac895d2d6d7 --- /dev/null +++ b/.changeset/pre/page-endpoint-route-ids.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: add `PageRouteId` and `EndpointRouteId` to `$app/types` diff --git a/.changeset/pre/paths-origin.md b/.changeset/pre/paths-origin.md new file mode 100644 index 000000000000..d16c61eecd92 --- /dev/null +++ b/.changeset/pre/paths-origin.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/kit': major +'@sveltejs/adapter-node': major +--- + +breaking: add `kit.paths.origin` config option, remove `kit.prerender.origin` and the `adapter-node` `ORIGIN` environment variable diff --git a/.changeset/pre/perky-badgers-learn.md b/.changeset/pre/perky-badgers-learn.md new file mode 100644 index 000000000000..1a10e5f0f634 --- /dev/null +++ b/.changeset/pre/perky-badgers-learn.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": major +--- + +breaking: move `Page`, `ReadonlyURL` and `ReadonlyURLSearchParams` from `@sveltejs/kit` to `$app/state` + \ No newline at end of file diff --git a/.changeset/pre/pink-cobras-rush.md b/.changeset/pre/pink-cobras-rush.md new file mode 100644 index 000000000000..2eee1de363e6 --- /dev/null +++ b/.changeset/pre/pink-cobras-rush.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: reinstate `$app/environment` as an alias for `$app/env`, in case dependencies import it diff --git a/.changeset/pre/plain-jobs-flash.md b/.changeset/pre/plain-jobs-flash.md new file mode 100644 index 000000000000..600633abee7a --- /dev/null +++ b/.changeset/pre/plain-jobs-flash.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: better tsconfig validation diff --git a/.changeset/pre/plenty-cougars-wave.md b/.changeset/pre/plenty-cougars-wave.md new file mode 100644 index 000000000000..03d6b75dd276 --- /dev/null +++ b/.changeset/pre/plenty-cougars-wave.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-cloudflare': patch +--- + +chore: check the `WORKERS_CI` environment variable to determine if we're building for Cloudflare Workers diff --git a/.changeset/pre/plenty-wasps-hang.md b/.changeset/pre/plenty-wasps-hang.md new file mode 100644 index 000000000000..45947bf988b9 --- /dev/null +++ b/.changeset/pre/plenty-wasps-hang.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/enhanced-img': patch +--- + +chore: bump `vite-imagetools` to v12 diff --git a/.changeset/pre/polite-lemons-refuse.md b/.changeset/pre/polite-lemons-refuse.md new file mode 100644 index 000000000000..053810bb3e1d --- /dev/null +++ b/.changeset/pre/polite-lemons-refuse.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/adapter-netlify': major +'@sveltejs/adapter-vercel': major +--- + +breaking: edge function build target is now `es2022` diff --git a/.changeset/pre/prerelease-peer-deps.md b/.changeset/pre/prerelease-peer-deps.md new file mode 100644 index 000000000000..9b0a69fe24fa --- /dev/null +++ b/.changeset/pre/prerelease-peer-deps.md @@ -0,0 +1,9 @@ +--- +'@sveltejs/adapter-auto': patch +'@sveltejs/adapter-cloudflare': patch +'@sveltejs/adapter-node': patch +'@sveltejs/adapter-static': patch +'@sveltejs/adapter-vercel': patch +--- + +fix: allow prerelease versions of SvelteKit 3 to satisfy the peer dependency range diff --git a/.changeset/pre/public-items-agree.md b/.changeset/pre/public-items-agree.md new file mode 100644 index 000000000000..0c2243447cd7 --- /dev/null +++ b/.changeset/pre/public-items-agree.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: add `dirty()` property to form fields diff --git a/.changeset/pre/quick-badgers-notice.md b/.changeset/pre/quick-badgers-notice.md new file mode 100644 index 000000000000..eac206f9e905 --- /dev/null +++ b/.changeset/pre/quick-badgers-notice.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: remove `$app/stores` diff --git a/.changeset/pre/quick-cookies-guarded.md b/.changeset/pre/quick-cookies-guarded.md new file mode 100644 index 000000000000..fd2dd735afc2 --- /dev/null +++ b/.changeset/pre/quick-cookies-guarded.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: error when reading non-serialized `set-cookie` headers via `getSetCookie` in `load` diff --git a/.changeset/pre/quick-forms-merge.md b/.changeset/pre/quick-forms-merge.md new file mode 100644 index 000000000000..f1c79564bcbf --- /dev/null +++ b/.changeset/pre/quick-forms-merge.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +perf: avoid quadratic remote form issue merging diff --git a/.changeset/pre/quiet-aliases-retire.md b/.changeset/pre/quiet-aliases-retire.md new file mode 100644 index 000000000000..2938a89e4049 --- /dev/null +++ b/.changeset/pre/quiet-aliases-retire.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: deprecate the `alias` option diff --git a/.changeset/pre/quiet-bodies-repeat.md b/.changeset/pre/quiet-bodies-repeat.md new file mode 100644 index 000000000000..92950c357f78 --- /dev/null +++ b/.changeset/pre/quiet-bodies-repeat.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: don't attempt to serialize fetch responses when the request body is not a string or TypedArray diff --git a/.changeset/pre/quiet-masks-shop.md b/.changeset/pre/quiet-masks-shop.md new file mode 100644 index 000000000000..2be51678bfaa --- /dev/null +++ b/.changeset/pre/quiet-masks-shop.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: handle rejected streamed server data after delayed loads diff --git a/.changeset/pre/quiet-phones-bet.md b/.changeset/pre/quiet-phones-bet.md new file mode 100644 index 000000000000..149a0a33b763 --- /dev/null +++ b/.changeset/pre/quiet-phones-bet.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-cloudflare': major +--- + +breaking: remove use of the Cloudflare Workers Cache API in favour of Workers Caching diff --git a/.changeset/pre/quiet-ravens-smile.md b/.changeset/pre/quiet-ravens-smile.md new file mode 100644 index 000000000000..cfac9eb02bf4 --- /dev/null +++ b/.changeset/pre/quiet-ravens-smile.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: render pages over sibling endpoints without GET or HEAD handlers diff --git a/.changeset/pre/rare-lions-reply.md b/.changeset/pre/rare-lions-reply.md new file mode 100644 index 000000000000..88e765354b0e --- /dev/null +++ b/.changeset/pre/rare-lions-reply.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-node': patch +--- + +fix: correctly bundle entrypoints on Windows diff --git a/.changeset/pre/re-refresh-reruns.md b/.changeset/pre/re-refresh-reruns.md new file mode 100644 index 000000000000..06c92044fceb --- /dev/null +++ b/.changeset/pre/re-refresh-reruns.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: prevent infinite loops when server-side queries refresh each other in a cycle during the single-flight drain diff --git a/.changeset/pre/ready-chairs-fold.md b/.changeset/pre/ready-chairs-fold.md new file mode 100644 index 000000000000..b44cb58ea001 --- /dev/null +++ b/.changeset/pre/ready-chairs-fold.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/enhanced-img": patch +--- + +chore: bump `zimmerframe` to 1.1.4 + \ No newline at end of file diff --git a/.changeset/pre/ready-dragons-mix.md b/.changeset/pre/ready-dragons-mix.md new file mode 100644 index 000000000000..877d3fee119c --- /dev/null +++ b/.changeset/pre/ready-dragons-mix.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: populate `version` in service workers diff --git a/.changeset/pre/red-masks-remain.md b/.changeset/pre/red-masks-remain.md new file mode 100644 index 000000000000..f6c87e2418f4 --- /dev/null +++ b/.changeset/pre/red-masks-remain.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +feat: add field.touched() helper to remote form fields diff --git a/.changeset/pre/refresh-page-state.md b/.changeset/pre/refresh-page-state.md new file mode 100644 index 000000000000..f69d1876700a --- /dev/null +++ b/.changeset/pre/refresh-page-state.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: add `refreshAll` and deprecate `invalidateAll` diff --git a/.changeset/pre/remote-error-status.md b/.changeset/pre/remote-error-status.md new file mode 100644 index 000000000000..cf588fc9d7f8 --- /dev/null +++ b/.changeset/pre/remote-error-status.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: read the error status of `App.Error` in the `prerender` and `query.live` remote functions diff --git a/.changeset/pre/remote-exports-prebundle.md b/.changeset/pre/remote-exports-prebundle.md new file mode 100644 index 000000000000..8feaf30b0ec8 --- /dev/null +++ b/.changeset/pre/remote-exports-prebundle.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: resolve remote modules as external during dev prebundling so packages can re-export remote functions diff --git a/.changeset/pre/remote-functions-no-withresolvers.md b/.changeset/pre/remote-functions-no-withresolvers.md new file mode 100644 index 000000000000..9162194f55db --- /dev/null +++ b/.changeset/pre/remote-functions-no-withresolvers.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: avoid `Promise.withResolvers` in client remote functions for older browser support diff --git a/.changeset/pre/remote-functions-required-flag.md b/.changeset/pre/remote-functions-required-flag.md new file mode 100644 index 000000000000..c049456a34e7 --- /dev/null +++ b/.changeset/pre/remote-functions-required-flag.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: disallow `*.remote.ts/js` files unless `experimental.remoteFunctions` is enabled diff --git a/.changeset/pre/remote-prerender-error-propagation.md b/.changeset/pre/remote-prerender-error-propagation.md new file mode 100644 index 000000000000..72c9c8a879de --- /dev/null +++ b/.changeset/pre/remote-prerender-error-propagation.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: propagate errors from prerendered remote responses instead of re-running the function diff --git a/.changeset/pre/rename-noscroll-keepfocus-reset.md b/.changeset/pre/rename-noscroll-keepfocus-reset.md new file mode 100644 index 000000000000..9bc4615d3ab9 --- /dev/null +++ b/.changeset/pre/rename-noscroll-keepfocus-reset.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: replace the `noScroll` and `keepFocus` options of `goto` with a single `reset` option, and the `data-sveltekit-noscroll` and `data-sveltekit-keepfocus` attributes with `data-sveltekit-reset` diff --git a/.changeset/pre/reserved-keyword-remote-exports.md b/.changeset/pre/reserved-keyword-remote-exports.md new file mode 100644 index 000000000000..89919a7b0923 --- /dev/null +++ b/.changeset/pre/reserved-keyword-remote-exports.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: allow reserved words (e.g. `delete`, `class`) as remote function export names diff --git a/.changeset/pre/reset-failed-boundary-on-navigation.md b/.changeset/pre/reset-failed-boundary-on-navigation.md new file mode 100644 index 000000000000..1f5d0b793fa0 --- /dev/null +++ b/.changeset/pre/reset-failed-boundary-on-navigation.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: reset failed `` on client navigation so a stale `+error.svelte` is torn down diff --git a/.changeset/pre/resolve-route-escape-sequences.md b/.changeset/pre/resolve-route-escape-sequences.md new file mode 100644 index 000000000000..0387f6c9c660 --- /dev/null +++ b/.changeset/pre/resolve-route-escape-sequences.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: expand `[x+nn]` and `[u+nnnn]` escape sequences when resolving a route id to a pathname diff --git a/.changeset/pre/respond-with-error-status.md b/.changeset/pre/respond-with-error-status.md new file mode 100644 index 000000000000..abefa63dfaa2 --- /dev/null +++ b/.changeset/pre/respond-with-error-status.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: respect the status returned from `handleError` on the fallback error page served to error-page sub-requests diff --git a/.changeset/pre/rich-poems-happen.md b/.changeset/pre/rich-poems-happen.md new file mode 100644 index 000000000000..2489bcfa6da0 --- /dev/null +++ b/.changeset/pre/rich-poems-happen.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: warn if there are plugins using `transformIndexHtml` diff --git a/.changeset/pre/ripe-boats-think.md b/.changeset/pre/ripe-boats-think.md new file mode 100644 index 000000000000..25396d2de64d --- /dev/null +++ b/.changeset/pre/ripe-boats-think.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": major +--- + +breaking: don't abort navigation when calling `invalidate(All)` during navigation + \ No newline at end of file diff --git a/.changeset/pre/ripe-months-count.md b/.changeset/pre/ripe-months-count.md new file mode 100644 index 000000000000..e56903a11a1a --- /dev/null +++ b/.changeset/pre/ripe-months-count.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: consistent special filename patterns diff --git a/.changeset/pre/ripe-tires-chew.md b/.changeset/pre/ripe-tires-chew.md new file mode 100644 index 000000000000..17a8c85a9fd4 --- /dev/null +++ b/.changeset/pre/ripe-tires-chew.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": major +--- + +breaking: allow `handleError` to influence status code + \ No newline at end of file diff --git a/.changeset/pre/ripe-trains-dance.md b/.changeset/pre/ripe-trains-dance.md new file mode 100644 index 000000000000..5b737326fa27 --- /dev/null +++ b/.changeset/pre/ripe-trains-dance.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: delegate CORS handling to Vite for static directory requests during development diff --git a/.changeset/pre/rolldown-min-version.md b/.changeset/pre/rolldown-min-version.md new file mode 100644 index 000000000000..04daa1b806f6 --- /dev/null +++ b/.changeset/pre/rolldown-min-version.md @@ -0,0 +1,9 @@ +--- +'@sveltejs/kit': major +'@sveltejs/enhanced-img': major +'@sveltejs/adapter-node': major +'@sveltejs/adapter-netlify': major +'@sveltejs/adapter-vercel': major +--- + +breaking: require `vite@^8.0.12`, the first Vite 8 release bundling stable `rolldown` 1.0.0 diff --git a/.changeset/pre/route-changed-after-error-page.md b/.changeset/pre/route-changed-after-error-page.md new file mode 100644 index 000000000000..b6601a40d164 --- /dev/null +++ b/.changeset/pre/route-changed-after-error-page.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: refetch route-tracking server data when navigating away from an error page diff --git a/.changeset/pre/route-id-bracket-escapes.md b/.changeset/pre/route-id-bracket-escapes.md new file mode 100644 index 000000000000..750bf027c2b8 --- /dev/null +++ b/.changeset/pre/route-id-bracket-escapes.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: allow routes to contain `[` and `]` via the `[x+5b]` and `[x+5d]` escapes diff --git a/.changeset/pre/routeid-only-real-routes.md b/.changeset/pre/routeid-only-real-routes.md new file mode 100644 index 000000000000..014f1c6e160e --- /dev/null +++ b/.changeset/pre/routeid-only-real-routes.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: only include routes with a `+page` or `+server` in `RouteId` diff --git a/.changeset/pre/salty-chefs-sleep.md b/.changeset/pre/salty-chefs-sleep.md new file mode 100644 index 000000000000..1f2cb46beafb --- /dev/null +++ b/.changeset/pre/salty-chefs-sleep.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/package': patch +'@sveltejs/kit': patch +--- + +chore: remove dependency on kleur diff --git a/.changeset/pre/serialize-query-set-during-ssr.md b/.changeset/pre/serialize-query-set-during-ssr.md new file mode 100644 index 000000000000..6fb1f9932a99 --- /dev/null +++ b/.changeset/pre/serialize-query-set-during-ssr.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: serialize `query(...).set(...)`/`query(...).refresh()` values into the rendered HTML when called from within a query during SSR diff --git a/.changeset/pre/set-response-fixed-bodies.md b/.changeset/pre/set-response-fixed-bodies.md new file mode 100644 index 000000000000..9c38b4f96b0f --- /dev/null +++ b/.changeset/pre/set-response-fixed-bodies.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: derive `content-length` from fixed response bodies in `setResponse` diff --git a/.changeset/pre/shiny-feet-crash.md b/.changeset/pre/shiny-feet-crash.md new file mode 100644 index 000000000000..0d1485671007 --- /dev/null +++ b/.changeset/pre/shiny-feet-crash.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +chore: deprecate `Response` helpers in favor of platform-provided alternatives diff --git a/.changeset/pre/shiny-lines-guard.md b/.changeset/pre/shiny-lines-guard.md new file mode 100644 index 000000000000..7a8523ab1cf4 --- /dev/null +++ b/.changeset/pre/shiny-lines-guard.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: don't destroy partial-line app output with the prerender progress line diff --git a/.changeset/pre/silent-bushes-grow.md b/.changeset/pre/silent-bushes-grow.md new file mode 100644 index 000000000000..d2befebda630 --- /dev/null +++ b/.changeset/pre/silent-bushes-grow.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: surface prerender errors during development diff --git a/.changeset/pre/silent-dogs-warn.md b/.changeset/pre/silent-dogs-warn.md new file mode 100644 index 000000000000..b6c4e94fe52e --- /dev/null +++ b/.changeset/pre/silent-dogs-warn.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": patch +--- + +chore: bump `cookie` to 2.0.1 + \ No newline at end of file diff --git a/.changeset/pre/silly-islands-greet.md b/.changeset/pre/silly-islands-greet.md new file mode 100644 index 000000000000..1168f1bf419e --- /dev/null +++ b/.changeset/pre/silly-islands-greet.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: add `cookies.parse` method diff --git a/.changeset/pre/six-items-obey.md b/.changeset/pre/six-items-obey.md new file mode 100644 index 000000000000..1fa5752378f5 --- /dev/null +++ b/.changeset/pre/six-items-obey.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: require Node 22.17 diff --git a/.changeset/pre/sixty-clubs-show.md b/.changeset/pre/sixty-clubs-show.md new file mode 100644 index 000000000000..3b47604e3385 --- /dev/null +++ b/.changeset/pre/sixty-clubs-show.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-cloudflare': patch +--- + +fix: avoid caching immutable asset 404s diff --git a/.changeset/pre/sixty-dragons-hide.md b/.changeset/pre/sixty-dragons-hide.md new file mode 100644 index 000000000000..ad02352ba196 --- /dev/null +++ b/.changeset/pre/sixty-dragons-hide.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/adapter-netlify": patch +"@sveltejs/adapter-node": patch +--- + +chore: bump `rolldown` to 1.2.3 diff --git a/.changeset/pre/sixty-words-strive.md b/.changeset/pre/sixty-words-strive.md new file mode 100644 index 000000000000..ad5ae4e633ad --- /dev/null +++ b/.changeset/pre/sixty-words-strive.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: deprecate `error(status, {...})` in favour of `error(status, message, {...})` diff --git a/.changeset/pre/smart-pugs-refuse.md b/.changeset/pre/smart-pugs-refuse.md new file mode 100644 index 000000000000..88b7bbf216ec --- /dev/null +++ b/.changeset/pre/smart-pugs-refuse.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: detect `$app/server` and `$app/env/private` client imports when SvelteKit is installed inside the project root diff --git a/.changeset/pre/snapshot-deprecate.md b/.changeset/pre/snapshot-deprecate.md new file mode 100644 index 000000000000..2b61da240ea3 --- /dev/null +++ b/.changeset/pre/snapshot-deprecate.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +chore: deprecate `export const snapshot` in favour of the `snapshot` helper diff --git a/.changeset/pre/snapshot-helper.md b/.changeset/pre/snapshot-helper.md new file mode 100644 index 000000000000..0b571c50f25c --- /dev/null +++ b/.changeset/pre/snapshot-helper.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: add `snapshot` helper to `$app/navigation` diff --git a/.changeset/pre/soft-ants-work.md b/.changeset/pre/soft-ants-work.md new file mode 100644 index 000000000000..b342f72dd64f --- /dev/null +++ b/.changeset/pre/soft-ants-work.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: support custom values in `page.state` via `transport` hook diff --git a/.changeset/pre/some-groups-return.md b/.changeset/pre/some-groups-return.md new file mode 100644 index 000000000000..a7ee2cb52233 --- /dev/null +++ b/.changeset/pre/some-groups-return.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: forbid external redirects by default diff --git a/.changeset/pre/sort-manifest-readdir.md b/.changeset/pre/sort-manifest-readdir.md new file mode 100644 index 000000000000..4a660f4563c8 --- /dev/null +++ b/.changeset/pre/sort-manifest-readdir.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: sort directory entries when building the route manifest so node indices are deterministic across runtimes (e.g. Bun and Node) diff --git a/.changeset/pre/sparkly-candies-serve.md b/.changeset/pre/sparkly-candies-serve.md new file mode 100644 index 000000000000..0c48b6d8765a --- /dev/null +++ b/.changeset/pre/sparkly-candies-serve.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: skip unnecessary `version.json` checks if `updated.current` is already `true` diff --git a/.changeset/pre/sparkly-kids-shine.md b/.changeset/pre/sparkly-kids-shine.md new file mode 100644 index 000000000000..7562e41ac6b0 --- /dev/null +++ b/.changeset/pre/sparkly-kids-shine.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: allow generation of $app/tsconfig without TypeScript installed diff --git a/.changeset/pre/spicy-drinks-build.md b/.changeset/pre/spicy-drinks-build.md new file mode 100644 index 000000000000..453c6dc1b288 --- /dev/null +++ b/.changeset/pre/spicy-drinks-build.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/kit": patch +--- + +chore: remove dependency on `set-cookie-parser` diff --git a/.changeset/pre/spicy-taxis-jump.md b/.changeset/pre/spicy-taxis-jump.md new file mode 100644 index 000000000000..d2c293ae410a --- /dev/null +++ b/.changeset/pre/spicy-taxis-jump.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: fall back to the page's form actions when a sibling endpoint has no POST handler diff --git a/.changeset/pre/spotty-pandas-brake.md b/.changeset/pre/spotty-pandas-brake.md new file mode 100644 index 000000000000..ce4c21736855 --- /dev/null +++ b/.changeset/pre/spotty-pandas-brake.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +perf: use a `Set` to check element ids when validating fragment links during prerendering diff --git a/.changeset/pre/spotty-pandas-wait.md b/.changeset/pre/spotty-pandas-wait.md new file mode 100644 index 000000000000..975e838fa90c --- /dev/null +++ b/.changeset/pre/spotty-pandas-wait.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/adapter-netlify': patch +'@sveltejs/adapter-vercel': patch +--- + +fix: await `init` on every request to prevent race condition diff --git a/.changeset/pre/stable-version-build-hashes.md b/.changeset/pre/stable-version-build-hashes.md new file mode 100644 index 000000000000..b961c466d5d9 --- /dev/null +++ b/.changeset/pre/stable-version-build-hashes.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: preserve shared client chunk hashes when the app version changes diff --git a/.changeset/pre/stale-monkeys-type.md b/.changeset/pre/stale-monkeys-type.md new file mode 100644 index 000000000000..069703e657e7 --- /dev/null +++ b/.changeset/pre/stale-monkeys-type.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: include hoisted packages in Vite's `server.fs.allow` list diff --git a/.changeset/pre/strict-bees-arrive.md b/.changeset/pre/strict-bees-arrive.md new file mode 100644 index 000000000000..9943a53c21cd --- /dev/null +++ b/.changeset/pre/strict-bees-arrive.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: separate adapter Vite plugins into `pre` and `post` diff --git a/.changeset/pre/sw-manifest-immutable.md b/.changeset/pre/sw-manifest-immutable.md new file mode 100644 index 000000000000..99fae75cd78a --- /dev/null +++ b/.changeset/pre/sw-manifest-immutable.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: only include `_app/immutable` files in `$app/manifest`'s `immutable` in the service worker diff --git a/.changeset/pre/swift-otters-fork.md b/.changeset/pre/swift-otters-fork.md new file mode 100644 index 000000000000..5a68377573e7 --- /dev/null +++ b/.changeset/pre/swift-otters-fork.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: remove the dead `SSRState.fallback` field and name the server state fork semantics diff --git a/.changeset/pre/tall-frogs-argue.md b/.changeset/pre/tall-frogs-argue.md new file mode 100644 index 000000000000..54c3f7723264 --- /dev/null +++ b/.changeset/pre/tall-frogs-argue.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/kit': patch +'@sveltejs/package': patch +--- + +fix: import resolved peer dependencies as file URLs so project-relative resolution works on Windows diff --git a/.changeset/pre/tame-cookies-relax.md b/.changeset/pre/tame-cookies-relax.md new file mode 100644 index 000000000000..873edfff833e --- /dev/null +++ b/.changeset/pre/tame-cookies-relax.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +fix: default cookies to `secure` to `false` during development diff --git a/.changeset/pre/tangy-flies-pay.md b/.changeset/pre/tangy-flies-pay.md new file mode 100644 index 000000000000..64bb06f5745b --- /dev/null +++ b/.changeset/pre/tangy-flies-pay.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: log errors caught by the Vite dev server handler diff --git a/.changeset/pre/tangy-llamas-attack.md b/.changeset/pre/tangy-llamas-attack.md new file mode 100644 index 000000000000..83cf86f5c081 --- /dev/null +++ b/.changeset/pre/tangy-llamas-attack.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-netlify': patch +--- + +fix: import the generated manifest via `pathToFileURL`, which handles special characters in the project path diff --git a/.changeset/pre/ten-lands-send.md b/.changeset/pre/ten-lands-send.md new file mode 100644 index 000000000000..e870ae34ded9 --- /dev/null +++ b/.changeset/pre/ten-lands-send.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: remove param files in folder in favor of `params.js/ts` file diff --git a/.changeset/pre/ten-signs-sing.md b/.changeset/pre/ten-signs-sing.md new file mode 100644 index 000000000000..d66f7146a58c --- /dev/null +++ b/.changeset/pre/ten-signs-sing.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": major +--- + +breaking: move env-related types to `@sveltejs/kit/env` + \ No newline at end of file diff --git a/.changeset/pre/ten-snakes-show.md b/.changeset/pre/ten-snakes-show.md new file mode 100644 index 000000000000..5e4cf65f7260 --- /dev/null +++ b/.changeset/pre/ten-snakes-show.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": patch +--- + +chore: bump `acorn` to 8.18.0 + \ No newline at end of file diff --git a/.changeset/pre/ten-worms-shine.md b/.changeset/pre/ten-worms-shine.md new file mode 100644 index 000000000000..af24fd3da5aa --- /dev/null +++ b/.changeset/pre/ten-worms-shine.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-vercel': major +--- + +breaking: remove support for edge and Node 20 runtimes diff --git a/.changeset/pre/thick-needles-fold.md b/.changeset/pre/thick-needles-fold.md new file mode 100644 index 000000000000..e4a5ca93c541 --- /dev/null +++ b/.changeset/pre/thick-needles-fold.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: resolve `root` per instance of the SvelteKit Vite plugin diff --git a/.changeset/pre/tidy-donkeys-shake.md b/.changeset/pre/tidy-donkeys-shake.md new file mode 100644 index 000000000000..6e2a444af4b5 --- /dev/null +++ b/.changeset/pre/tidy-donkeys-shake.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/package': patch +--- + +fix: emit declarations when the tsconfig lives above the package root diff --git a/.changeset/pre/tidy-hoops-brake.md b/.changeset/pre/tidy-hoops-brake.md new file mode 100644 index 000000000000..9a52bef88cec --- /dev/null +++ b/.changeset/pre/tidy-hoops-brake.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: ignore path casing differences when warning about overridden Vite config on Windows diff --git a/.changeset/pre/tidy-icons-rest.md b/.changeset/pre/tidy-icons-rest.md new file mode 100644 index 000000000000..1bb0cb62ef1e --- /dev/null +++ b/.changeset/pre/tidy-icons-rest.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: return a lightweight 404 instead of rendering the error page for subresource requests diff --git a/.changeset/pre/tidy-owls-preload.md b/.changeset/pre/tidy-owls-preload.md new file mode 100644 index 000000000000..c3a8fc8848e1 --- /dev/null +++ b/.changeset/pre/tidy-owls-preload.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: `preloadCode` now takes a route ID (e.g. `/blog/[slug]`) instead of a pathname. Route IDs are not prefixed with `paths.base` diff --git a/.changeset/pre/tidy-pages-route.md b/.changeset/pre/tidy-pages-route.md new file mode 100644 index 000000000000..ae2e8e934d28 --- /dev/null +++ b/.changeset/pre/tidy-pages-route.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-vercel': patch +--- + +fix: match sibling `.html__route.js` route resolution requests diff --git a/.changeset/pre/tidy-pumas-travel.md b/.changeset/pre/tidy-pumas-travel.md new file mode 100644 index 000000000000..cee4088e7800 --- /dev/null +++ b/.changeset/pre/tidy-pumas-travel.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/kit': patch +'@sveltejs/adapter-node': patch +--- + +fix: preserve stripped path prefixes by making trailing-slash redirects relative diff --git a/.changeset/pre/tidy-remote-form-search.md b/.changeset/pre/tidy-remote-form-search.md new file mode 100644 index 000000000000..f49c5355bafe --- /dev/null +++ b/.changeset/pre/tidy-remote-form-search.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: preserve the current URL search parameters when submitting a remote form without JavaScript diff --git a/.changeset/pre/tidy-remotes-own.md b/.changeset/pre/tidy-remotes-own.md new file mode 100644 index 000000000000..7f73c71f9ea3 --- /dev/null +++ b/.changeset/pre/tidy-remotes-own.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: only treat files in node_modules as remote modules when the package has a peer dependency on @sveltejs/kit diff --git a/.changeset/pre/tidy-remotes-shake.md b/.changeset/pre/tidy-remotes-shake.md new file mode 100644 index 000000000000..748ba8047014 --- /dev/null +++ b/.changeset/pre/tidy-remotes-shake.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: treeshake prerendered remote functions in the right chunks diff --git a/.changeset/pre/tidy-waves.md b/.changeset/pre/tidy-waves.md new file mode 100644 index 000000000000..462dd555f4ea --- /dev/null +++ b/.changeset/pre/tidy-waves.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: move `BeforeNavigate`, `OnNavigate`, `AfterNavigate`, `Navigation`, `NavigationTarget`, `NavigationType`, `GotoOptions` and the `Navigation*` variant types from `@sveltejs/kit` to `$app/navigation` diff --git a/.changeset/pre/tiny-cases-help.md b/.changeset/pre/tiny-cases-help.md new file mode 100644 index 000000000000..85d2c4ef3219 --- /dev/null +++ b/.changeset/pre/tiny-cases-help.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/package": patch +--- + +chore: bump `svelte2tsx` to 0.7.60 + \ No newline at end of file diff --git a/.changeset/pre/true-yaks-smell.md b/.changeset/pre/true-yaks-smell.md new file mode 100644 index 000000000000..99d72742e9df --- /dev/null +++ b/.changeset/pre/true-yaks-smell.md @@ -0,0 +1,7 @@ +--- +'@sveltejs/adapter-netlify': patch +'@sveltejs/adapter-node': patch +'@sveltejs/adapter-vercel': patch +--- + +chore: bump Rolldown to `1.2.0` diff --git a/.changeset/pre/tweak-types-dedup.md b/.changeset/pre/tweak-types-dedup.md new file mode 100644 index 000000000000..dc98d21ba82b --- /dev/null +++ b/.changeset/pre/tweak-types-dedup.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: deduplicate type-stripping logic in `tweak_types` diff --git a/.changeset/pre/twenty-actors-relax.md b/.changeset/pre/twenty-actors-relax.md new file mode 100644 index 000000000000..4923d09caf46 --- /dev/null +++ b/.changeset/pre/twenty-actors-relax.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: avoid infinite loop when building with `--watch` flag diff --git a/.changeset/pre/twenty-squids-tie.md b/.changeset/pre/twenty-squids-tie.md new file mode 100644 index 000000000000..89d354471e90 --- /dev/null +++ b/.changeset/pre/twenty-squids-tie.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/kit": major +--- + +breaking: move `ActionResult` and `SubmitFunction` from `@sveltejs/kit` to `$app/forms` + \ No newline at end of file diff --git a/.changeset/pre/upset-cities-vanish.md b/.changeset/pre/upset-cities-vanish.md new file mode 100644 index 000000000000..8cbaa19ed861 --- /dev/null +++ b/.changeset/pre/upset-cities-vanish.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: error during development if the adapter does not support instrumentation and it exists diff --git a/.changeset/pre/upset-needles-beg.md b/.changeset/pre/upset-needles-beg.md new file mode 100644 index 000000000000..68fbbc5ae54e --- /dev/null +++ b/.changeset/pre/upset-needles-beg.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: use mouseover+mousemove for preloading to reduce events diff --git a/.changeset/pre/validation-errors-handle-error.md b/.changeset/pre/validation-errors-handle-error.md new file mode 100644 index 000000000000..4be9e70b2300 --- /dev/null +++ b/.changeset/pre/validation-errors-handle-error.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: remove `handleValidationError` and pass remote function validation errors to `handleError` with `kind: 'validation'` diff --git a/.changeset/pre/vary-only-when-precompressed.md b/.changeset/pre/vary-only-when-precompressed.md new file mode 100644 index 000000000000..6a543c91002d --- /dev/null +++ b/.changeset/pre/vary-only-when-precompressed.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-node': patch +--- + +fix: don't send `Vary: Accept-Encoding` for assets that were never precompressed diff --git a/.changeset/pre/violet-planets-tell.md b/.changeset/pre/violet-planets-tell.md new file mode 100644 index 000000000000..b3a5fa38f208 --- /dev/null +++ b/.changeset/pre/violet-planets-tell.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: write generated tsconfig to `node_modules/$app/tsconfig.json` so that tools with simplified tsconfig resolution can find it diff --git a/.changeset/pre/wacky-mice-attack.md b/.changeset/pre/wacky-mice-attack.md new file mode 100644 index 000000000000..5e6d531bf847 --- /dev/null +++ b/.changeset/pre/wacky-mice-attack.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/adapter-cloudflare": patch +--- + +chore: bump `@cloudflare/worker-types` to 5.20260809.1 + \ No newline at end of file diff --git a/.changeset/pre/whole-buckets-clean.md b/.changeset/pre/whole-buckets-clean.md new file mode 100644 index 000000000000..71b7326958b0 --- /dev/null +++ b/.changeset/pre/whole-buckets-clean.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: more informative error message when running a command inside a query or prerender function diff --git a/.changeset/pre/wicked-olives-fold.md b/.changeset/pre/wicked-olives-fold.md new file mode 100644 index 000000000000..1c9e47e47be2 --- /dev/null +++ b/.changeset/pre/wicked-olives-fold.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + + fix: adjust error overload for optional `App.Error` parameters diff --git a/.changeset/pre/wide-mangos-admire.md b/.changeset/pre/wide-mangos-admire.md new file mode 100644 index 000000000000..da5acefa53e0 --- /dev/null +++ b/.changeset/pre/wide-mangos-admire.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': major +--- + +breaking: remove deprecated `.run()` method from live queries diff --git a/.changeset/pre/wild-donkeys-listen.md b/.changeset/pre/wild-donkeys-listen.md new file mode 100644 index 000000000000..001f7165fc91 --- /dev/null +++ b/.changeset/pre/wild-donkeys-listen.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: prerender and crawl pages whose `content-type` header carries a `charset` parameter diff --git a/.changeset/pre/wild-steaks-visit.md b/.changeset/pre/wild-steaks-visit.md new file mode 100644 index 000000000000..e70fd2893870 --- /dev/null +++ b/.changeset/pre/wild-steaks-visit.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: warn when naively proxying requests that result in responses with a `content-encoding` header diff --git a/.changeset/pre/wise-moons-read.md b/.changeset/pre/wise-moons-read.md new file mode 100644 index 000000000000..839ccf11669d --- /dev/null +++ b/.changeset/pre/wise-moons-read.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: stream promised `read` results lazily instead of eagerly buffering them diff --git a/.changeset/pre/wise-pumas-deny.md b/.changeset/pre/wise-pumas-deny.md new file mode 100644 index 000000000000..ff0b7d4a2f4a --- /dev/null +++ b/.changeset/pre/wise-pumas-deny.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/package': patch +--- + +fix: declare typescript as an optional peer dependency so svelte-package works under strict node-linkers diff --git a/.changeset/pre/witty-moons-smile.md b/.changeset/pre/witty-moons-smile.md new file mode 100644 index 000000000000..2b665846fe86 --- /dev/null +++ b/.changeset/pre/witty-moons-smile.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: correctly decode `[u+nnnn]` escape sequences above `ffff` diff --git a/.changeset/query-method-server-export.md b/.changeset/query-method-server-export.md new file mode 100644 index 000000000000..b2c8d54886ff --- /dev/null +++ b/.changeset/query-method-server-export.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/kit": minor +--- + +feat: support the `QUERY` HTTP method in `+server.js` diff --git a/.changeset/quiet-pans-shake.md b/.changeset/quiet-pans-shake.md new file mode 100644 index 000000000000..42155f926df6 --- /dev/null +++ b/.changeset/quiet-pans-shake.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: don't crash on interactions inside a form whose controls shadow `nodeName` diff --git a/.changeset/shared-read-range.md b/.changeset/shared-read-range.md new file mode 100644 index 000000000000..f3b11b92b6a1 --- /dev/null +++ b/.changeset/shared-read-range.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: correctly read zero-length files at the end of a binary form payload diff --git a/.changeset/soft-poems.md b/.changeset/soft-poems.md new file mode 100644 index 000000000000..3825ab8f1dbf --- /dev/null +++ b/.changeset/soft-poems.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: wait for the redirect navigation before prerendered remote functions resolve diff --git a/.changeset/spicy-planes-refactor.md b/.changeset/spicy-planes-refactor.md new file mode 100644 index 000000000000..7324f8778ab5 --- /dev/null +++ b/.changeset/spicy-planes-refactor.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: share the nearest error page walk between client and server diff --git a/.changeset/unify-walk-helpers.md b/.changeset/unify-walk-helpers.md new file mode 100644 index 000000000000..ad1a984c2d85 --- /dev/null +++ b/.changeset/unify-walk-helpers.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: unify the `walk` and `list_files` filesystem helpers diff --git a/.eslint/no-exports-to-runtime-imports.js b/.eslint/no-exports-to-runtime-imports.js new file mode 100644 index 000000000000..5a1873531882 --- /dev/null +++ b/.eslint/no-exports-to-runtime-imports.js @@ -0,0 +1,52 @@ +// @ts-expect-error no types here +import path from 'node:path'; + +/** + * @type {import('eslint').Rule.RuleModule} + */ +export default { + meta: { + type: 'problem', + docs: { + description: 'disallow imports from src/exports to src/runtime', + category: 'Possible Errors', + recommended: true + }, + schema: [], + messages: { + noExportsToRuntimeImport: + 'Imports from src/exports to src/runtime are not allowed. Move shared code outside src/runtime instead.' + } + }, + + create(context) { + const runtime_path = path.resolve(import.meta.dirname, '../packages/kit/src/runtime'); + + /** @param {import('estree').Literal} node */ + function check(node) { + const import_path = node.value; + if (typeof import_path !== 'string') return; + + const is_runtime_subpath = import_path.startsWith('#app/'); + const is_relative_runtime_import = + import_path.startsWith('.') && + path + .relative(runtime_path, path.resolve(path.dirname(context.filename), import_path)) + .split(path.sep)[0] !== '..'; + + if (is_runtime_subpath || is_relative_runtime_import) { + context.report({ + node, + messageId: 'noExportsToRuntimeImport' + }); + } + } + + return { + ImportDeclaration: (node) => check(node.source), + ExportNamedDeclaration: (node) => node.source && check(node.source), + ExportAllDeclaration: (node) => check(node.source), + ImportExpression: (node) => check(node.source) + }; + } +}; diff --git a/.eslint/require-path-to-file-url.js b/.eslint/require-path-to-file-url.js new file mode 100644 index 000000000000..81f206ca8a38 --- /dev/null +++ b/.eslint/require-path-to-file-url.js @@ -0,0 +1,89 @@ +/** + * @type {import('eslint').Rule.RuleModule} + */ +export default { + meta: { + type: 'problem', + docs: { + description: 'require dynamic imports of computed file paths to go through pathToFileURL' + }, + schema: [], + messages: { + requirePathToFileURL: + 'Dynamic import() of a computed path must be wrapped in pathToFileURL(...).href — on Windows, absolute paths parse as URLs with the drive letter as protocol and fail to import. See https://github.com/sveltejs/kit/issues/16620' + } + }, + + create(context) { + /** + * Returns true if the expression is safe to pass to `import()`: + * a static specifier, a relative computed specifier, or a value + * wrapped in `pathToFileURL`/`new URL` + * @param {import('estree').Node} node + * @param {number} depth + * @returns {boolean} + */ + function is_safe(node, depth = 0) { + if (node.type === 'Literal') { + return true; + } + + if (node.type === 'TemplateLiteral') { + if (node.expressions.length === 0) return true; + + // a computed specifier with a static relative prefix resolves + // relative to the importing module — no drive letters involved + const prefix = node.quasis[0].value.cooked ?? ''; + return prefix.startsWith('./') || prefix.startsWith('../'); + } + + if (node.type === 'CallExpression' || node.type === 'NewExpression') { + const callee = node.callee; + if (callee.type !== 'Identifier') return false; + + // single-argument new URL(path) misparses absolute windows paths just like import(), so require a base + return ( + callee.name === 'pathToFileURL' || (callee.name === 'URL' && node.arguments.length > 1) + ); + } + + // `pathToFileURL(...).href` and `new URL(...).href` + if (node.type === 'MemberExpression') { + return ( + node.property.type === 'Identifier' && + node.property.name === 'href' && + is_safe(node.object, depth) + ); + } + + // resolve an identifier one hop to its declaration + if (node.type === 'Identifier' && depth === 0) { + const variable = context.sourceCode + .getScope(node) + .references.find((ref) => ref.identifier === node)?.resolved; + + const def = variable?.defs[0]; + + if ( + variable?.defs.length === 1 && + def?.type === 'Variable' && + def.node.init && + // a reassignment after a safe initializer would not be seen here + variable.references.every((ref) => !ref.isWrite() || ref.init) + ) { + return is_safe(def.node.init, depth + 1); + } + } + + return false; + } + + return { + ImportExpression(node) { + if (!is_safe(node.source)) { + context.report({ node: node.source, messageId: 'requirePathToFileURL' }); + } + } + }; + } +}; diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index a94d39153bd8..1b48302b9232 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -25,7 +25,7 @@ body: id: logs attributes: label: Logs - description: "Please include browser console and server logs around the time this bug occurred. Please try not to insert an image but copy paste the log text." + description: 'Please include browser console and server logs around the time this bug occurred. Please try not to insert an image but copy paste the log text.' render: Shell - type: textarea id: system-info diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 5324344585ba..65c4f44f76c8 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,4 +1,4 @@ -name: "Feature Request" +name: 'Feature Request' description: Suggest an idea for this project body: - type: markdown diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index fcff7dc51487..b4e383020db8 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -5,14 +5,17 @@ closes # @@ -404,7 +401,7 @@ export async function fallback({ request }) { `+server.js` files can be placed in the same directory as `+page` files, allowing the same route to be either a page or an API endpoint. To determine which, SvelteKit applies the following rules: -- `PUT`/`PATCH`/`DELETE`/`OPTIONS` requests are always handled by `+server.js` since they do not apply to pages +- `PUT`/`PATCH`/`DELETE`/`OPTIONS`/`QUERY` requests are always handled by `+server.js` since they do not apply to pages - `GET`/`POST`/`HEAD` requests are treated as page requests if the `accept` header prioritises `text/html` (in other words, it's a browser page request), else they are handled by `+server.js`. - Responses to `GET` requests will include a `Vary: Accept` header, so that proxies and browsers cache HTML and JSON responses separately. @@ -449,7 +446,9 @@ You can read more about omitting `$types` in our [blog post](/blog/zero-config-t Any other files inside a route directory are ignored by SvelteKit. This means you can colocate components and utility modules with the routes that need them. -If components and modules are needed by multiple routes, it's a good idea to put them in [`$lib`]($lib). +Files such as +page.test.js, +server.spec.ts and +page.stories.svelte are also ignored, allowing tests and stories to be colocated with route files. + +If components and modules are needed by multiple routes, it's a good idea to put them in [`#lib`]($lib). ## Further reading diff --git a/documentation/docs/20-core-concepts/20-load.md b/documentation/docs/20-core-concepts/20-load.md index 7477f45c47b0..733f7c15a471 100644 --- a/documentation/docs/20-core-concepts/20-load.md +++ b/documentation/docs/20-core-concepts/20-load.md @@ -51,13 +51,13 @@ A more realistic version of your blog post's `load` function, that only runs on ```js /// file: src/routes/blog/[slug]/+page.server.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function getPost(slug: string): Promise<{ title: string, content: string }> } // @filename: index.js // ---cut--- -import * as db from '$lib/server/database'; +import * as db from '#lib/server/database.js'; /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { @@ -76,13 +76,13 @@ Your `+layout.svelte` files can also load data, via `+layout.js` or `+layout.ser ```js /// file: src/routes/blog/[slug]/+layout.server.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function getPostSummaries(): Promise> } // @filename: index.js // ---cut--- -import * as db from '$lib/server/database'; +import * as db from '#lib/server/database.js'; /** @type {import('./$types').LayoutServerLoad} */ export async function load() { @@ -171,10 +171,6 @@ In some cases, we might need the opposite — a parent layout might need to acce Type information for `page.data` is provided by `App.PageData`. -> [!LEGACY] -> `$app/state` was added in SvelteKit 2.12. If you're using an earlier version or are using Svelte 4, use `$app/stores` instead. -> It provides a `page` store with the same interface that you can subscribe to, e.g. `$page.data.title`. - ## Universal vs server As we've seen, there are two types of `load` function: @@ -296,18 +292,18 @@ export async function load({ fetch, params }) { ## Cookies -A server `load` function can get and set [`cookies`](@sveltejs-kit#Cookies). +A server `load` function can get [`cookies`](@sveltejs-kit#Cookies) as shown below. When setting cookies, SvelteKit provides default values for `httpOnly`, `secure`, and `path` — as described in [the API documentation](@sveltejs-kit#Cookies) — in order to improve security and developer experience. ```js /// file: src/routes/+layout.server.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function getUser(sessionid: string | undefined): Promise<{ name: string, avatar: string }> } // @filename: index.js // ---cut--- -import * as db from '$lib/server/database'; +import * as db from '#lib/server/database.js'; /** @type {import('./$types').LayoutServerLoad} */ export async function load({ cookies }) { @@ -425,7 +421,7 @@ export async function load({ params, parent }) { ## Errors -If an error is thrown during `load`, the nearest [`+error.svelte`](routing#error) will be rendered. For [_expected_](errors#Expected-errors) errors, use the `error` helper from `@sveltejs/kit` to specify the HTTP status code and an optional message: +If an error is thrown during `load`, the nearest [`+error.svelte`](routing#error) will be rendered. For [app errors](errors#App-errors), use the `error` helper from `@sveltejs/kit` to specify the HTTP status code and an optional message: ```js /// file: src/routes/admin/+layout.server.js @@ -457,7 +453,7 @@ export function load({ locals }) { Calling `error(...)` will throw an exception, making it easy to stop execution from inside helper functions. -If an [_unexpected_](errors#Unexpected-errors) error is thrown, SvelteKit will invoke [`handleError`](hooks#handleError) and treat it as a 500 Internal Error. +Every error is passed to the [`handleError`](hooks#handleError) hook. An [unknown error](errors#Unknown-errors) is treated as a 500 Internal Error unless the hook says otherwise. > [!NOTE] [In SvelteKit 1.x](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you) you had to `throw` the error yourself @@ -582,13 +578,13 @@ For example, given a pair of `load` functions like these... ```js /// file: src/routes/blog/[slug]/+page.server.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function getPost(slug: string): Promise<{ title: string, content: string }> } // @filename: index.js // ---cut--- -import * as db from '$lib/server/database'; +import * as db from '#lib/server/database.js'; /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { @@ -601,13 +597,13 @@ export async function load({ params }) { ```js /// file: src/routes/blog/[slug]/+layout.server.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function getPostSummaries(): Promise> } // @filename: index.js // ---cut--- -import * as db from '$lib/server/database'; +import * as db from '#lib/server/database.js'; /** @type {import('./$types').LayoutServerLoad} */ export async function load() { @@ -642,7 +638,9 @@ export async function load({ untrack, url }) { ### Manual invalidation -You can also rerun `load` functions that apply to the current page using [`invalidate(url)`]($app-navigation#invalidate), which reruns all `load` functions that depend on `url`, and [`invalidateAll()`]($app-navigation#invalidateAll), which reruns every `load` function. Server load functions will never automatically depend on a fetched `url` to avoid leaking secrets to the client. +You can also rerun `load` functions that apply to the current page using [`invalidate(url)`]($app-navigation#invalidate), which reruns all `load` functions that depend on `url`, and [`refreshAll()`]($app-navigation#refreshAll), which reruns every `load` function and all active queries. Server load functions will never automatically depend on a fetched `url` to avoid leaking secrets to the client. + +> [!NOTE] `refreshAll` does _not_ reset `page.state`, unlike its deprecated predecessor `invalidateAll`. A `load` function depends on `url` if it calls `fetch(url)` or `depends(url)`. Note that `url` can be a custom identifier that starts with `[a-z]:`: @@ -665,7 +663,7 @@ export async function load({ fetch, depends }) { ```svelte @@ -693,7 +691,7 @@ To summarize, a `load` function will rerun in the following situations: - It calls `await parent()` and a parent `load` function reran - A child `load` function calls `await parent()` and is rerunning, and the parent is a server load function - It declared a dependency on a specific URL via [`fetch`](#Making-fetch-requests) (universal load only) or [`depends`](@sveltejs-kit#LoadEvent), and that URL was marked invalid with [`invalidate(url)`]($app-navigation#invalidate) -- All active `load` functions were forcibly rerun with [`invalidateAll()`]($app-navigation#invalidateAll) +- All active `load` functions were forcibly rerun with [`refreshAll()`]($app-navigation#refreshAll) `params` and `url` can change in response to a `` link click, a [`
` interaction](form-actions#GET-vs-POST), a [`goto`]($app-navigation#goto) invocation, or a [`redirect`](@sveltejs-kit#redirect). @@ -758,7 +756,7 @@ Now, you can call `requireLogin` in any `load` function (or [form action](form-a /// file: +page.server.js // @filename: ambient.d.ts -declare module '$lib/server/auth' { +declare module '#lib/server/auth.js' { interface User { name: string; } @@ -768,7 +766,7 @@ declare module '$lib/server/auth' { // @filename: index.ts // ---cut--- -import { requireLogin } from '$lib/server/auth'; +import { requireLogin } from '#lib/server/auth.js'; export function load() { const user = requireLogin(); diff --git a/documentation/docs/20-core-concepts/30-form-actions.md b/documentation/docs/20-core-concepts/30-form-actions.md index be0af6c990d3..1a3d2ad03e40 100644 --- a/documentation/docs/20-core-concepts/30-form-actions.md +++ b/documentation/docs/20-core-concepts/30-form-actions.md @@ -50,6 +50,8 @@ We can also invoke the action from other pages (for example if there's a login w
``` +Submitting this form takes you to `/login`, whether the action succeeds or fails — that's what the browser does, and [`use:enhance`](#Progressive-enhancement-use:enhance) emulates it. If you'd rather stay where you are and handle the result in place, there's a recipe for that in the [progressive enhancement](#Progressive-enhancement-use:enhance) section. + ## Named actions Instead of one `default` action, a page can have as many named actions as it needs: @@ -107,11 +109,11 @@ Each action receives a `RequestEvent` object, allowing you to read the data with ```js /// file: src/routes/login/+page.server.js // @filename: ambient.d.ts -declare module '$lib/server/db'; +declare module '#lib/server/db.js'; // @filename: index.js // ---cut--- -import * as db from '$lib/server/db'; +import * as db from '#lib/server/db.js'; /** @type {import('./$types').PageServerLoad} */ export async function load({ cookies }) { @@ -165,15 +167,17 @@ export const actions = { If the request couldn't be processed because of invalid data, you can return validation errors — along with the previously submitted form values — back to the user so that they can try again. The `fail` function lets you return an HTTP status code (typically 400 or 422, in the case of validation errors) along with the data. The status code is available through `page.status` and the data through `form`: +When using `use:enhance` (or making a `fetch` request with the `accept: application/json` header), the HTTP response status code will match the status code passed to `fail`. When an action returns data, the response will have status 200, and when it returns nothing (i.e. `undefined`), the response will have status 204. This makes it easier to track form submission outcomes using observability tools. + ```js /// file: src/routes/login/+page.server.js // @filename: ambient.d.ts -declare module '$lib/server/db'; +declare module '#lib/server/db.js'; // @filename: index.js // ---cut--- +++import { fail } from '@sveltejs/kit';+++ -import * as db from '$lib/server/db'; +import * as db from '#lib/server/db.js'; /** @satisfies {import('./$types').Actions} */ export const actions = { @@ -232,12 +236,12 @@ Redirects (and errors) work exactly the same as in [`load`](load#Redirects): // @errors: 2345 /// file: src/routes/login/+page.server.js // @filename: ambient.d.ts -declare module '$lib/server/db'; +declare module '#lib/server/db.js'; // @filename: index.js // ---cut--- import { fail, +++redirect+++ } from '@sveltejs/kit'; -import * as db from '$lib/server/db'; +import * as db from '#lib/server/db.js'; /** @satisfies {import('./$types').Actions} */ export const actions = { @@ -297,7 +301,7 @@ export {}; // @filename: index.js // ---cut--- -/** @type {import('@sveltejs/kit').Handle} */ +/** @type {import('@sveltejs/kit/hooks').Handle} */ export async function handle({ event, resolve }) { event.locals.user = await getUser(event.cookies.get('sessionid')); return resolve(event); @@ -359,13 +363,37 @@ The easiest way to progressively enhance a form is to add the `use:enhance` acti Without an argument, `use:enhance` will emulate the browser-native behaviour, just without the full-page reloads. It will: -- update the `form` property, `page.form` and `page.status` on a successful or invalid response, but only if the action is on the same page you're submitting from. For example, if your form looks like `
`, the `form` prop and the `page.form` state will _not_ be updated. This is because in the native form submission case you would be redirected to the page the action is on. If you want to have them updated either way, use [`applyAction`](#Progressive-enhancement-Customising-use:enhance) +- if the action is on the page you're submitting from, emulate a reload of the current page — updating the `form` property, `page.form` and `page.status` on a successful or invalid response +- if the action is on a different page (for example ``), navigate to the action's URL on a successful _or_ invalid response, populating that page's `form` property and `page.status`, just as a native submission would. A new history entry is pushed - reset the `` element -- invalidate all data using `invalidateAll` on a successful response +- refresh all data using `refreshAll` on a successful response - call `goto` on a redirect response -- render the nearest `+error` boundary if an error occurs +- render the nearest `+error` boundary if an unexpected error occurs — the boundary nearest the _action's_ route, if the action is on a different page - [reset focus](accessibility#Focus-management) to the appropriate element +> [!NOTE] Two details are worth knowing about the URL: +> +> Where a submission lands is `result.location` — the form's `action` with the `?/actionName` parameter removed, since that parameter has done its job once the action has run. `action="/login?/do-login"` lands on `/login`, and any other query parameters are kept, so `action="/login?redirectTo=/dashboard&/do-login"` lands on `/login?redirectTo=/dashboard`. The destination's `load` functions see that URL via `url.search`. +> +> Two consequences worth knowing: +> +> - A _relative_ action replaces the entire query string as a side effect of URL resolution — `action="?/delete"` on `/items?page=2` resolves to `/items?/delete`, so both enhanced and native submissions drop `page=2`. Write `action="?page=2&/delete"` to preserve it. +> - Without JavaScript the browser can't strip anything, so an unenhanced submission leaves you on the raw action URL (`/login?/do-login`). Reloading that URL is a plain `GET`, which doesn't re-run the action and renders the page normally, so the difference is cosmetic — but a `load` function can observe a different `url.search` depending on whether JavaScript is available. + +If you don't want a cross-page submission to navigate — a common need for login or newsletter widgets that live in a layout and post to another page's action — pass `navigate: false` to `update`, which gives you the same behaviour as SvelteKit 2: + +```svelte + async ({ update }) => { + await update({ navigate: false }); + }} +> +``` + +The result is applied to the page you're on: `form`, `page.form` and `page.status` are updated, and error results render the current route's nearest `+error` boundary. + ### Customising use:enhance To customise the behaviour, you can provide a `SubmitFunction` that runs immediately before the form is submitted, and (optionally) returns a callback that runs with the `ActionResult`. @@ -390,7 +418,7 @@ To customise the behaviour, you can provide a `SubmitFunction` that runs immedia You can use these functions to show and hide loading UI, and so on. -If you return a callback, you override the default post-submission behavior. To get it back, call `update`, which accepts `invalidateAll` and `reset` parameters, or use `applyAction` on the result: +If you return a callback, you override the default post-submission behavior. To get it back, call `update`, which accepts `navigate`, `refreshAll` and `reset` parameters, or use `applyAction` on the result: ```svelte /// file: src/routes/login/+page.svelte @@ -416,13 +444,19 @@ If you return a callback, you override the default post-submission behavior. To > ``` +> [!NOTE] `update` accepts these options: +> +> - `reset: false` if you don't want the `` values to be reset after a successful submission +> - `refreshAll` controls whether all data is refreshed after submission. It defaults to `true` for successful results and `false` for failures. When the submission navigates to another page, setting it to `false` does _not_ prevent the destination's own `load` functions from running — it only allows shared layout data to be reused. `invalidateAll` is a deprecated alias for `refreshAll` +> - `navigate: false` applies a non-redirect result to the current page instead of navigating to `result.location`. Redirects are always followed + The behaviour of `applyAction(result)` depends on `result.type`: -- `success`, `failure` — sets `page.status` to `result.status` and updates `form` and `page.form` to `result.data` (regardless of where you are submitting from, in contrast to `update` from `enhance`) -- `redirect` — calls `goto(result.location, { invalidateAll: true })` -- `error` — renders the nearest `+error` boundary with `result.error` +- `success`, `failure` — sets `page.status` to `result.status` and updates `form` and `page.form` to `result.data` +- `redirect` — calls `goto(result.location, { refreshAll: true })` +- `error` — renders the current route's nearest `+error` boundary with `result.error` -In all cases, [focus will be reset](accessibility#Focus-management). +In all cases, [focus will be reset](accessibility#Focus-management). `applyAction` does not navigate to the `location` of a non-redirect result or refresh data. Use `update` to get the complete default enhanced behavior, including carrying form data and status into a cross-page navigation. When implementing enhancement manually, inspect `result.location` and use [`goto`]($app-navigation#goto) or [`refreshAll`]($app-navigation#refreshAll) as appropriate. ### Custom event listener @@ -431,7 +465,7 @@ We can also implement progressive enhancement ourselves, without `use:enhance`, ```svelte @@ -520,7 +557,7 @@ Some forms don't need to `POST` data to the server — search inputs, for exampl
``` -Submitting this form will navigate to `/search?q=...` and invoke your load function but will not invoke an action. As with `
` elements, you can set the [`data-sveltekit-reload`](link-options#data-sveltekit-reload), [`data-sveltekit-replacestate`](link-options#data-sveltekit-replacestate), [`data-sveltekit-keepfocus`](link-options#data-sveltekit-keepfocus) and [`data-sveltekit-noscroll`](link-options#data-sveltekit-noscroll) attributes on the `
` to control the router's behaviour. +Submitting this form will navigate to `/search?q=...` and invoke your load function but will not invoke an action. As with `` elements, you can set the [`data-sveltekit-reload`](link-options#data-sveltekit-reload), [`data-sveltekit-replacestate`](link-options#data-sveltekit-replacestate) and [`data-sveltekit-reset`](link-options#data-sveltekit-reset) attributes on the `` to control the router's behaviour. ## Further reading diff --git a/documentation/docs/20-core-concepts/40-page-options.md b/documentation/docs/20-core-concepts/40-page-options.md index d9023c341eee..3136792378cf 100644 --- a/documentation/docs/20-core-concepts/40-page-options.md +++ b/documentation/docs/20-core-concepts/40-page-options.md @@ -33,9 +33,9 @@ export const prerender = 'auto'; > [!NOTE] If your entire app is suitable for prerendering, you can use [`adapter-static`](adapter-static), which will output files suitable for use with any static webserver. -The prerenderer will start at the root of your app and generate files for any prerenderable pages or `+server.js` routes it finds. Each page is scanned for `` elements that point to other pages that are candidates for prerendering — because of this, you generally don't need to specify which pages should be accessed. If you _do_ need to specify which pages should be accessed by the prerenderer, you can do so with [`config.kit.prerender.entries`](configuration#prerender), or by exporting an [`entries`](#entries) function from your dynamic route. +The prerenderer will start at the root of your app and generate files for any prerenderable pages or `+server.js` routes it finds. Each page is scanned for `` elements that point to other pages that are candidates for prerendering — because of this, you generally don't need to specify which pages should be accessed. If you _do_ need to specify which pages should be accessed by the prerenderer, you can do so with [`config.prerender.entries`](configuration#prerender), or by exporting an [`entries`](#entries) function from your dynamic route. -While prerendering, the value of `building` imported from [`$app/environment`]($app-environment) will be `true`. +While prerendering, the value of `building` imported from [`$app/env`]($app-env) will be `true`. ### Prerendering server routes @@ -80,7 +80,7 @@ If you encounter an error like 'The following routes were marked as prerenderabl Since these routes cannot be dynamically server-rendered, this will cause errors when people try to access the route in question. There are a few ways to fix it: -* Ensure that SvelteKit can find the route by following links from [`config.kit.prerender.entries`](configuration#prerender) or the [`entries`](#entries) page option. Add links to dynamic routes (i.e. pages with `[parameters]` ) to this option if they are not found through crawling the other entry points, else they are not prerendered because SvelteKit doesn't know what value the parameters should have. Pages not marked as prerenderable will be ignored and their links to other pages will not be crawled, even if some of them would be prerenderable. +* Ensure that SvelteKit can find the route by following links from [`config.prerender.entries`](configuration#prerender) or the [`entries`](#entries) page option. Add links to dynamic routes (i.e. pages with `[parameters]` ) to this option if they are not found through crawling the other entry points, else they are not prerendered because SvelteKit doesn't know what value the parameters should have. Pages not marked as prerenderable will be ignored and their links to other pages will not be crawled, even if some of them would be prerenderable. * Ensure that SvelteKit can find the route by discovering a link to it from one of your other prerendered pages that have server-side rendering enabled. * Change `export const prerender = true` to `export const prerender = 'auto'`. Routes with `'auto'` can be dynamically server rendered @@ -98,7 +98,7 @@ SvelteKit will discover pages to prerender automatically, by starting at _entry Most of the time, that's enough. In some situations, links to pages like `/blog/hello-world` might not exist (or might not exist on prerendered pages), in which case we need to tell SvelteKit about their existence. -This can be done with [`config.kit.prerender.entries`](configuration#prerender), or by exporting an `entries` function from a `+page.js`, a `+page.server.js` or a `+server.js` belonging to a dynamic route: +This can be done with [`config.prerender.entries`](configuration#prerender), or by exporting an `entries` function from a `+page.js`, a `+page.server.js` or a `+server.js` belonging to a dynamic route: ```js /// file: src/routes/blog/[slug]/+page.server.js @@ -153,7 +153,7 @@ You can enable `csr` during development (for example to take advantage of HMR) l ```js /// file: +page.js -import { dev } from '$app/environment'; +import { dev } from '$app/env'; export const csr = dev; ``` diff --git a/documentation/docs/20-core-concepts/50-state-management.md b/documentation/docs/20-core-concepts/50-state-management.md index 874bc6968e1b..22bca9c5304b 100644 --- a/documentation/docs/20-core-concepts/50-state-management.md +++ b/documentation/docs/20-core-concepts/50-state-management.md @@ -45,13 +45,13 @@ For the same reason, your `load` functions should be _pure_ — no side-effects ```js /// file: +page.js // @filename: ambient.d.ts -declare module '$lib/user' { +declare module '#lib/user.js' { export const user: { set: (value: any) => void }; } // @filename: index.js // ---cut--- -import { user } from '$lib/user'; +import { user } from '#lib/user.js'; /** @type {import('./$types').PageLoad} */ export async function load({ fetch }) { @@ -82,7 +82,7 @@ If you're not using SSR, then there's no risk of accidentally exposing one user' ## Using state and stores with context -You might wonder how we're able to use `page.data` and other [app state]($app-state) (or [app stores]($app-stores)) if we can't use global state. The answer is that app state and app stores on the server use Svelte's [context API](/tutorial/svelte/context-api) — the state (or store) is attached to the component tree with `setContext`, and when you subscribe you retrieve it with `getContext`. We can do the same thing with our own state: +You might wonder how we're able to use `page.data` and other [app state]($app-state) if we can't use global state. The answer is that app state and app stores on the server use Svelte's [context API](/tutorial/svelte/context-api) — the state (or store) is attached to the component tree with `setContext`, and when you subscribe you retrieve it with `getContext`. We can do the same thing with our own state: ```svelte diff --git a/documentation/docs/20-core-concepts/60-remote-functions.md b/documentation/docs/20-core-concepts/60-remote-functions.md index ec9a76f9a16e..7bb17d3da163 100644 --- a/documentation/docs/20-core-concepts/60-remote-functions.md +++ b/documentation/docs/20-core-concepts/60-remote-functions.md @@ -10,30 +10,32 @@ Remote functions are a tool for type-safe communication between client and serve Combined with Svelte's experimental support for [`await`](/docs/svelte/await-expressions), it allows you to load and manipulate data directly inside your components. -This feature is currently experimental, meaning it is likely to contain bugs and is subject to change without notice. You must opt in by adding the `compilerOptions.experimental.async` and `kit.experimental.remoteFunctions` options in your `svelte.config.js`: +This feature is currently experimental, meaning it is likely to contain bugs and is subject to change without notice. You must opt in by adding the `compilerOptions.experimental.async` and `experimental.remoteFunctions` options to the SvelteKit plugin in your `vite.config.js`: ```js -/// file: svelte.config.js -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - experimental: { - +++remoteFunctions: true+++ - } - }, - compilerOptions: { - experimental: { - +++async: true+++ - } - } -}; - -export default config; +/// file: vite.config.js +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + experimental: { + +++remoteFunctions: true+++ + }, + compilerOptions: { + experimental: { + +++async: true+++ + } + } + }) + ], +}); ``` ## Overview -Remote functions are exported from a `.remote.js` or `.remote.ts` file, and come in four flavours: `query`, `form`, `command` and `prerender`. On the client, the exported functions are transformed to `fetch` wrappers that invoke their counterparts on the server via a generated HTTP endpoint. Remote files can be placed anywhere in your `src` directory (except inside the `src/lib/server` directory), and third party libraries can provide them, too. +Remote functions are exported from a _remote module_, which is a file whose name includes a `remote` segment (e.g. `remote.ts` or `data.remote.ts`). They come in four flavours: `query`, `form`, `command` and `prerender`. On the client, the exported functions are transformed to `fetch` wrappers that invoke their counterparts on the server via a generated HTTP endpoint. Remote modules can be placed anywhere in your `src` directory (except inside a `server` directory — files here [cannot be imported into client-side code](server-only-modules)), and third-party libraries that list `@sveltejs/kit` in their `peerDependencies` can provide them too. ## query @@ -44,13 +46,13 @@ The `query` function allows you to read dynamic data from the server. ```js /// file: src/routes/blog/data.remote.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function sql(strings: TemplateStringsArray, ...values: any[]): Promise; } // @filename: index.js // ---cut--- import { query } from '$app/server'; -import * as db from '$lib/server/database'; +import * as db from '#lib/server/database.js'; export const getPosts = query(async () => { const posts = await db.sql` @@ -64,7 +66,7 @@ export const getPosts = query(async () => { }); ``` -> [!NOTE] Throughout this page, you'll see imports from fictional modules like `$lib/server/database` and `$lib/server/auth`. These are purely for illustrative purposes — you can use whatever database client and auth setup you like. +> [!NOTE] Throughout this page, you'll see imports from fictional modules like `#lib/server/database.js` and `#lib/server/auth.js`. These are purely for illustrative purposes — you can use whatever database client and auth setup you like. > > The `db.sql` function above is a [tagged template function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates) that escapes any interpolated values. @@ -137,7 +139,7 @@ Since `getPost` exposes an HTTP endpoint, it's important to validate this argume ```js /// file: src/routes/blog/data.remote.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function sql(strings: TemplateStringsArray, ...values: any[]): Promise; } // @filename: index.js @@ -145,7 +147,7 @@ declare module '$lib/server/database' { import * as v from 'valibot'; import { error } from '@sveltejs/kit'; import { query } from '$app/server'; -import * as db from '$lib/server/database'; +import * as db from '#lib/server/database.js'; export const getPosts = query(async () => { /* ... */ }); @@ -209,14 +211,14 @@ On the server, the callback receives an array of the arguments the function was ```js /// file: weather.remote.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function sql(strings: TemplateStringsArray, ...values: any[]): Promise; } // @filename: index.js // ---cut--- import * as v from 'valibot'; import { query } from '$app/server'; -import * as db from '$lib/server/database'; +import * as db from '#lib/server/database.js'; export const getWeather = query.batch(v.string(), async (cityIds) => { const weather = await db.sql` @@ -294,7 +296,7 @@ If you need direct, imperative access to the underlying stream of values (rather ```js // @filename: time.remote.ts -import { RemoteLiveQueryFunction } from '@sveltejs/kit'; +import type { RemoteLiveQueryFunction } from '$app/server'; export declare const getTime: RemoteLiveQueryFunction; // @errors: 2304 // @filename: index.js @@ -321,11 +323,11 @@ The `form` function makes it easy to write data to the server. It takes a callba ```ts /// file: src/routes/blog/data.remote.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function sql(strings: TemplateStringsArray, ...values: any[]): Promise; } -declare module '$lib/server/auth' { +declare module '#lib/server/auth.js' { interface User { name: string; } @@ -340,8 +342,8 @@ declare module '$lib/server/auth' { import * as v from 'valibot'; import { error, redirect } from '@sveltejs/kit'; import { query, form } from '$app/server'; -import * as db from '$lib/server/database'; -import * as auth from '$lib/server/auth'; +import * as db from '#lib/server/database.js'; +import * as auth from '#lib/server/auth.js'; export const getPosts = query(async () => { /* ... */ }); @@ -566,7 +568,7 @@ In addition to declarative schema validation, you can programmatically mark fiel // @errors: 18046 /// file: src/routes/shop/data.remote.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function buy(qty: number): Promise } // @filename: index.js @@ -574,7 +576,7 @@ declare module '$lib/server/database' { import * as v from 'valibot'; import { invalid } from '@sveltejs/kit'; import { form } from '$app/server'; -import * as db from '$lib/server/database'; +import * as db from '#lib/server/database.js'; export const buyHotcakes = form( v.object({ @@ -737,11 +739,11 @@ The example above uses [`redirect(...)`](@sveltejs-kit#redirect), which sends th ```ts /// file: src/routes/blog/data.remote.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function sql(strings: TemplateStringsArray, ...values: any[]): Promise; } -declare module '$lib/server/auth' { +declare module '#lib/server/auth.js' { interface User { name: string; } @@ -755,8 +757,8 @@ declare module '$lib/server/auth' { import * as v from 'valibot'; import { error, redirect } from '@sveltejs/kit'; import { query, form } from '$app/server'; -import * as db from '$lib/server/database'; -import * as auth from '$lib/server/auth'; +import * as db from '#lib/server/database.js'; +import * as auth from '#lib/server/auth.js'; export const getPosts = query(async () => { /* ... */ }); @@ -804,7 +806,7 @@ We can customize what happens when the form is submitted with the `enhance` meth

Create a new post

@@ -845,7 +847,7 @@ When each instance should render different values, pass them as the second argum

Todos

{#each await getTodos() as todo} - {@const modify = modifyTodo.for(todo.id)} + {const modify = modifyTodo.for(todo.id)} @@ -862,7 +864,7 @@ To accomplish this, add a field to your schema for the button value, and use `as ```svelte @@ -884,7 +886,7 @@ To accomplish this, add a field to your schema for the button value, and use `as In your form handler, you can check which button was clicked: ```js -/// file: $lib/auth.js +/// file: #lib/auth.js import * as v from 'valibot'; import { form } from '$app/server'; @@ -915,14 +917,14 @@ As with `query` and `form`, if the function accepts an argument, it should be [v ```ts /// file: likes.remote.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function sql(strings: TemplateStringsArray, ...values: any[]): Promise; } // @filename: index.js // ---cut--- import * as v from 'valibot'; import { query, command } from '$app/server'; -import * as db from '$lib/server/database'; +import * as db from '#lib/server/database.js'; export const getLikes = query(v.string(), async (id) => { const [row] = await db.sql` @@ -949,7 +951,7 @@ Now simply call `addLike`, from (for example) an event handler: @@ -1055,7 +1057,7 @@ Unfortunately, life isn't always as simple as the preceding example. The server SvelteKit makes this easy by allowing the client to _request_ that the server updates specific data using `submit().updates` (for `form`) or `myCommand().updates` (for `command`): ```ts -import type { RemoteQueryUpdate, RemoteQuery } from '@sveltejs/kit'; +import type { RemoteQueryUpdate, RemoteQuery } from '$app/server'; interface Post {} declare function submit(): Promise & { updates(...updates: RemoteQueryUpdate[]): Promise; @@ -1110,8 +1112,8 @@ export const createPost = form( Additionally, `requested` allows a simple shorthand when all you want to do is refresh the requested query instances: ```ts -import type { RemoteQueryFunction } from '@sveltejs/kit'; import { requested } from '$app/server'; +import type { RemoteQueryFunction } from '$app/server'; declare const getPosts: RemoteQueryFunction; // ---cut--- // this is the same as looping over the result and calling `void query.refresh()`. @@ -1130,13 +1132,13 @@ The `prerender` function is similar to `query`, except that it will be invoked a ```js /// file: src/routes/blog/data.remote.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function sql(strings: TemplateStringsArray, ...values: any[]): Promise; } // @filename: index.js // ---cut--- import { prerender } from '$app/server'; -import * as db from '$lib/server/database'; +import * as db from '#lib/server/database.js'; export const getPosts = prerender(async () => { const posts = await db.sql` @@ -1161,7 +1163,7 @@ As with queries, prerender functions can accept an argument, which should be [va ```js /// file: src/routes/blog/data.remote.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function sql(strings: TemplateStringsArray, ...values: any[]): Promise; } // @filename: index.js @@ -1169,7 +1171,7 @@ declare module '$lib/server/database' { import * as v from 'valibot'; import { error } from '@sveltejs/kit'; import { prerender } from '$app/server'; -import * as db from '$lib/server/database'; +import * as db from '#lib/server/database.js'; export const getPosts = prerender(async () => { /* ... */ }); @@ -1234,18 +1236,23 @@ As long as _you're_ not passing invalid data to your remote functions, there are - the function signature changed between deployments, and some users are currently on an older version of your app - someone is trying to attack your site by poking your exposed endpoints with bad data -In the second case, we don't want to give the attacker any help, so SvelteKit will generate a generic [400 Bad Request](https://http.dog/400) response. You can control the message by implementing the [`handleValidationError`](hooks#handleValidationError) server hook, which, like [`handleError`](hooks#handleError), must return an [`App.Error`](errors#Type-safety) (which defaults to `{ message: string }`): +In the second case, we don't want to give the attacker any help, so SvelteKit will generate a generic [400 Bad Request](https://http.dog/400) response. Validation failures pass through the server [`handleError`](hooks#handleError) hook with `kind: 'validation'`, an `error` object containing `{ status, message }`, and the validation `issues`. You can use the issues to log the failure or customise the response: ```js /// file: src/hooks.server.js -/** @type {import('@sveltejs/kit').HandleValidationError} */ -export function handleValidationError({ event, issues }) { - return { - message: 'Nice try, hacker!' - }; +/** @type {import('@sveltejs/kit/hooks').HandleServerError} */ +export function handleError({ kind, issues }) { + if (kind === 'validation') { + console.error(issues); + return { + message: 'Nice try, hacker!' + }; + } } ``` +Be thoughtful about exposing validation issues, as they may give an attacker useful information. Returning `error` unchanged is safe — unlike `issues`, it only contains the generic status and message. + If you know what you're doing and want to opt out of validation, you can pass the string `'unchecked'` in place of a schema: ```ts @@ -1270,14 +1277,14 @@ interface User { avatar: string; } -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function findUser(sessionId: string | undefined): Promise; } // @filename: index.js // ---cut--- import { getRequestEvent, query } from '$app/server'; -import { findUser } from '$lib/server/database'; +import { findUser } from '#lib/server/database.js'; export const getProfile = query(async () => { const user = await getUser(); @@ -1300,7 +1307,8 @@ const getUser = query(async () => { Note that some properties of `RequestEvent` are different inside remote functions: - you cannot set headers (other than writing cookies, and then only inside `form` and `command` functions) -- `route`, `params` and `url` relate to the page the remote function was called from, _not_ the URL of the endpoint SvelteKit creates for the remote function. Never use them to determine whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated. Queries are also not re-run when the user navigates (unless the argument to the query changes as a result of navigation), and so you should be mindful of how you use these values. +- inside `query` functions (including `query.batch` and `query.live`), accessing `route`, `params` or `url` throws an error. Queries are cached by their arguments and are not re-run when the user navigates, so results depending on these values would go stale without the cache noticing. Pass values from the page as arguments to the query instead. You can still derive trusted context from cookies or headers in the [`handle`](hooks#handle) hook and put it on `locals`, but note that for query requests `handle` sees the URL of the remote endpoint, not of the page +- in `form` and `command` functions, `route`, `params` and `url` relate to the page the remote function was called from, _not_ the URL of the endpoint SvelteKit creates for the remote function. Never use them to determine whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated ## Redirects diff --git a/documentation/docs/20-core-concepts/70-environment-variables.md b/documentation/docs/20-core-concepts/70-environment-variables.md index 7d27e16e7022..8b282fef7855 100644 --- a/documentation/docs/20-core-concepts/70-environment-variables.md +++ b/documentation/docs/20-core-concepts/70-environment-variables.md @@ -11,40 +11,17 @@ During development, and at build time, variables defined in a `.env` or `.env.lo API_KEY=19f401ba-e8b0-48c4-8c77-b0ebb26d97fe ``` -By default, every environment variable is implicitly available inside your app via the following modules: - -- [`$env/static/private`]($env-static-private) -- [`$env/static/public`]($env-static-public) -- [`$env/dynamic/private`]($env-dynamic-private) -- [`$env/dynamic/public`]($env-dynamic-public) - -## Explicit environment variables - -As of SvelteKit 2.63, you can opt into _explicit_ environment variables, in which case you instead import environment variables from these modules: +After following the setup below, they can be imported via the following modules: - [`$app/env/private`]($app-env-private) - [`$app/env/public`]($app-env-public) -Additionally, the [`$app/environment`]($app-environment) module is renamed to [`$app/env`]($app-env). - -> [!NOTE] Explicit environment variables will become the default in SvelteKit 3. The `$env/*` modules, along with `$app/environment`, will be removed. - -### Setup - -To opt in, update your configuration... +> [!LEGACY] +> The `$env/*` modules, along with `$app/environment` were deprecated in SvelteKit 3 (and will be removed in SvelteKit 4) in favour of explicit environment variables that were added in SvelteKit 2.62 as an experimental option. -```js -/// file: svelte.config.js -export default { - kit: { - experimental: { - +++explicitEnvironmentVariables: true+++ - } - } -}; -``` +## Setup -...and add a `src/env.ts` (or `src/env.js`) file that exports a `variables` object: +Add a `src/env.ts` (or `src/env.js`) file that exports a `variables` object: ```ts /// file: src/env.ts @@ -55,11 +32,11 @@ export const variables = defineEnvVars({ }); ``` -Each value in the object passed to [`defineEnvVars`](@sveltejs-kit-env#defineEnvVars) is an [`EnvVarConfig`](@sveltejs-kit#EnvVarConfig) object that configures the environment variable. +Each value in the object passed to [`defineEnvVars`](@sveltejs-kit-env#defineEnvVars) is an [`EnvVarConfig`](@sveltejs-kit-env#EnvVarConfig) object that configures the environment variable. > [!NOTE] `defineEnvVars` returns its argument unaltered — it exists purely to help with type safety. -### Private variables +## Private variables By default, all variables are considered private. For example, you don't want to reveal your `API_KEY`: @@ -82,7 +59,7 @@ import { API_KEY } from '$app/env/private'; The `$app/env/private` module cannot be imported into code that runs in the browser, so that you can't accidentally reveal your secrets in a JavaScript bundle. -### Public variables +## Public variables Some variables are perfectly safe — necessary, even — to expose to the browser. For these, we can specify `public: true`: @@ -127,7 +104,7 @@ export const variables = defineEnvVars({ ``` -### Validation +## Validation You can specify a [Standard Schema](https://standardschema.dev/) validator such as [Zod](https://zod.dev/) or [Valibot](https://valibot.dev/) to check that an environment variable value is correct: @@ -144,6 +121,23 @@ export const variables = defineEnvVars({ }); ``` +If you don't want to bring in a schema library, you can pass a function that returns the (possibly transformed) value, or throws an error explaining the problem: + +```ts +/// file: src/env.ts +import { defineEnvVars } from '@sveltejs/kit/env'; + +export const variables = defineEnvVars({ + GOOGLE_ANALYTICS_ID: { + public: true, + schema: (value) => { + if (!value?.startsWith('G-')) throw new Error('expected a Google Analytics ID'); + return value; + } + } +}); +``` + If a value is invalid, the app will fail to start (or build). To opt out of one or the other, use [`building`]($app-env#building) from `$app/env` along with a validator that accepts an optional value: ```ts @@ -162,7 +156,7 @@ export const variables = defineEnvVars({ You can use validators to make values optional, or transform them (such as turning a string into a boolean, or parsing JSON) — see your validation library's documentation to learn how. -### Static variables +## Static variables By default, variables are dynamic. If a variable is configured with `static: true`, it will be inlined into your application code, enabling optimisations like dead-code elimination: @@ -190,7 +184,7 @@ Because this variable is `static`, the `` component shown here wil ```svelte {#if SHOW_DEBUG_OVERLAY} @@ -206,7 +200,7 @@ SHOW_DEBUG_OVERLAY=true npm run build ...then the component will be included and shown. -### Documenting variables +## Documenting variables You can document the purpose of an environment variable by adding a `description`: diff --git a/documentation/docs/25-build-and-deploy/10-building-your-app.md b/documentation/docs/25-build-and-deploy/10-building-your-app.md index ca3fd211fbc9..263e73ad793c 100644 --- a/documentation/docs/25-build-and-deploy/10-building-your-app.md +++ b/documentation/docs/25-build-and-deploy/10-building-your-app.md @@ -10,11 +10,11 @@ Secondly, an _adapter_ takes this production build and tunes it for your target ## During the build -SvelteKit will load your `+page/layout(.server).js` files (and all files they import) for analysis during the build. Any code that should _not_ be executed at this stage must check that `building` from [`$app/environment`]($app-environment) is `false`: +SvelteKit will load your `+page/layout(.server).js` files (and all files they import) for analysis during the build. Any code that should _not_ be executed at this stage must check that `building` from [`$app/env`]($app-env) is `false`: ```js -+++import { building } from '$app/environment';+++ -import { initialiseDatabase } from '$lib/server/database'; ++++import { building } from '$app/env';+++ +import { initialiseDatabase } from '#lib/server/database.js'; +++if (!building) {+++ initialiseDatabase(); diff --git a/documentation/docs/25-build-and-deploy/20-adapters.md b/documentation/docs/25-build-and-deploy/20-adapters.md index 0e6062a5101a..ae9999044ccb 100644 --- a/documentation/docs/25-build-and-deploy/20-adapters.md +++ b/documentation/docs/25-build-and-deploy/20-adapters.md @@ -16,33 +16,32 @@ Additional [community-provided adapters](/packages#sveltekit-adapters) exist for ## Using adapters -Your adapter is specified in `svelte.config.js`: +Your adapter is specified in `vite.config.js`: ```js -/// file: svelte.config.js +/// file: vite.config.js // @filename: ambient.d.ts declare module 'svelte-adapter-foo' { - const adapter: (opts: any) => import('@sveltejs/kit').Adapter; + const adapter: (opts?: any) => import('@sveltejs/kit').Adapter; export default adapter; } +// @errors: 2554 // @filename: index.js // ---cut--- -import adapter from 'svelte-adapter-foo'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter({ - // adapter options go here +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; ++++import adapter from 'svelte-adapter-foo';+++ + +export default defineConfig({ + plugins: [ + sveltekit({ + +++adapter: adapter()+++ }) - } -}; - -export default config; + ] +}); ``` ## Platform-specific context Some adapters may have access to additional information about the request. For example, Cloudflare Workers can access an `env` object containing KV namespaces etc. This can be passed to the `RequestEvent` used in [hooks](hooks) and [server routes](routing#server) as the `platform` property — consult each adapter's documentation to learn more. - diff --git a/documentation/docs/25-build-and-deploy/40-adapter-node.md b/documentation/docs/25-build-and-deploy/40-adapter-node.md index 18102d03d363..29ca294f9c57 100644 --- a/documentation/docs/25-build-and-deploy/40-adapter-node.md +++ b/documentation/docs/25-build-and-deploy/40-adapter-node.md @@ -6,21 +6,22 @@ To generate a standalone Node server, use [`adapter-node`](https://github.com/sv ## Usage -Install with `npm i -D @sveltejs/adapter-node`, then add the adapter to your `svelte.config.js`: +Install with `npm i -D @sveltejs/adapter-node`, then add the adapter to your `vite.config.js`: ```js -// @errors: 2307 -/// file: svelte.config.js +// @errors: 2307 2554 +/// file: vite.config.js import adapter from '@sveltejs/adapter-node'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter() - } -}; - -export default config; +export default defineConfig({ + plugins: [ + sveltekit({ + adapter: adapter() + }) + ] +}); ``` ## Deploying @@ -33,7 +34,7 @@ You will need the output directory, the project's `package.json`, and the produc node build ``` -Development dependencies will be bundled into your app using [Rollup](https://rollupjs.org). To control whether a given package is bundled or externalised, place it in `devDependencies` or `dependencies` respectively in your `package.json`. +Development dependencies will be bundled into your app using [Rolldown](https://rolldown.rs/). To control whether a given package is bundled or externalised, place it in `devDependencies` or `dependencies` respectively in your `package.json`. ### Compressing responses @@ -77,18 +78,34 @@ Alternatively, the server can be configured to accept connections on a specified SOCKET_PATH=/tmp/socket node build ``` -### `ORIGIN`, `PROTOCOL_HEADER`, `HOST_HEADER`, and `PORT_HEADER` +### `PROTOCOL_HEADER`, `HOST_HEADER`, and `PORT_HEADER` -HTTP doesn't give SvelteKit a reliable way to know the URL that is currently being requested. The simplest way to tell SvelteKit where the app is being served is to set the `ORIGIN` environment variable: +HTTP doesn't give SvelteKit a reliable way to know the URL that is currently being requested. By default, SvelteKit will derive the origin from the request's `host` header (and the `https` protocol, if no `PROTOCOL_HEADER` is set). -```sh -ORIGIN=https://my.site node build +If your app is served from an origin that isn't known at request time — for example because it's behind a reverse proxy that doesn't pass the `host` header, or because you want to use a canonical origin for CSRF checks that differs from the request's host — you can set the [`paths.origin`](configuration#paths) option in your `vite.config.js`: -# or e.g. for local previewing and testing -ORIGIN=http://localhost:3000 node build +```js +// @errors: 2307 +/// file: vite.config.js +import adapter from '@sveltejs/adapter-node'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + adapter: adapter(), + paths: { + origin: process.env.ORIGIN + } + }) + ] +}); ``` -With this, a request for the `/stuff` pathname will correctly resolve to `https://my.site/stuff`. Alternatively, you can specify headers that tell SvelteKit about the request protocol and host, from which it can construct the origin URL: +When `paths.origin` is not set (the default), `adapter-node` derives the origin from the request — using the `host` header (and `PROTOCOL_HEADER`/`PORT_HEADER` if set) — and sets `request.url` accordingly. Otherwise, the configured value is used as the trusted self-origin for CSRF checks on form submissions and remote function calls, and as the value of `url.origin` during prerendering. + +Alternatively, you can specify headers that tell SvelteKit about the request protocol and host, from which it can construct the origin URL: ```sh PROTOCOL_HEADER=x-forwarded-proto HOST_HEADER=x-forwarded-host node build @@ -149,23 +166,24 @@ The number of seconds for [`keepAliveTimeout`](https://nodejs.org/api/http.html# The adapter can be configured with various options: ```js -// @errors: 2307 -/// file: svelte.config.js +// @errors: 2307 2554 +/// file: vite.config.js import adapter from '@sveltejs/adapter-node'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter({ - // default options are shown - out: 'build', - precompress: true, - envPrefix: '' +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + adapter: adapter({ + // default options are shown + out: 'build', + precompress: true, + envPrefix: '' + }) }) - } -}; - -export default config; + ] +}); ``` ### out @@ -187,7 +205,6 @@ envPrefix: 'MY_CUSTOM_'; ```sh MY_CUSTOM_HOST=127.0.0.1 \ MY_CUSTOM_PORT=4000 \ -MY_CUSTOM_ORIGIN=https://my.site \ node build ``` @@ -206,8 +223,8 @@ You can listen to the `sveltekit:shutdown` event which is emitted after the HTTP ```js // @errors: 2304 process.on('sveltekit:shutdown', async (reason) => { - await jobs.stop(); - await db.close(); + await jobs.stop(); + await db.close(); }); ``` @@ -274,6 +291,6 @@ app.listen(3000, () => { }); ``` -> [!NOTE] When you use `handler.js` in a custom server, only the environment variables read by the handler itself take effect: `ORIGIN`, `PROTOCOL_HEADER`, `HOST_HEADER`, `PORT_HEADER`, `ADDRESS_HEADER`, `XFF_DEPTH`, and `BODY_SIZE_LIMIT`. +> [!NOTE] When you use `handler.js` in a custom server, only the environment variables read by the handler itself take effect: `PROTOCOL_HEADER`, `HOST_HEADER`, `PORT_HEADER`, `ADDRESS_HEADER`, `XFF_DEPTH`, and `BODY_SIZE_LIMIT`. > > The server-lifecycle variables (`PORT`, `HOST`, `SOCKET_PATH`, `SHUTDOWN_TIMEOUT`, `IDLE_TIMEOUT`, `KEEP_ALIVE_TIMEOUT`, `HEADERS_TIMEOUT`, `LISTEN_PID`, `LISTEN_FDS`) are only honored by the default `node build` server. Implement them yourself in a custom server if you need the same behavior — for example, the snippet above listens on a hardcoded `3000` regardless of `PORT`. diff --git a/documentation/docs/25-build-and-deploy/50-adapter-static.md b/documentation/docs/25-build-and-deploy/50-adapter-static.md index d6f5e8f249b7..35c1e1e06b5e 100644 --- a/documentation/docs/25-build-and-deploy/50-adapter-static.md +++ b/documentation/docs/25-build-and-deploy/50-adapter-static.md @@ -8,29 +8,30 @@ This will prerender your entire site as a collection of static files. If you'd l ## Usage -Install with `npm i -D @sveltejs/adapter-static`, then add the adapter to your `svelte.config.js`: +Install with `npm i -D @sveltejs/adapter-static`, then add the adapter to your `vite.config.js`: ```js // @errors: 2307 -/// file: svelte.config.js +/// file: vite.config.js import adapter from '@sveltejs/adapter-static'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter({ - // default options are shown. On some platforms - // these options are set automatically — see below - pages: 'build', - assets: 'build', - fallback: undefined, - precompress: false, - strict: true +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + adapter: adapter({ + // default options are shown. On some platforms + // these options are set automatically — see below + pages: 'build', + assets: 'build', + fallback: undefined, + precompress: false, + strict: true + }) }) - } -}; - -export default config; + ] +}); ``` ...and add the [`prerender`](page-options#prerender) option to your root layout: @@ -57,17 +58,18 @@ On these platforms, you should omit the adapter options so that `adapter-static` ```js // @errors: 2307 -/// file: svelte.config.js +/// file: vite.config.js import adapter from '@sveltejs/adapter-static'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter(---{...}---) - } -}; - -export default config; +export default defineConfig({ + plugins: [ + sveltekit({ + adapter: adapter(---{...}---) + }) + ] +}); ``` ## Options @@ -96,7 +98,7 @@ By default, `adapter-static` checks that either all pages and endpoints (if any) ## GitHub Pages -When building for [GitHub Pages](https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages), if your repo name is not equivalent to `your-username.github.io`, make sure to update [`config.kit.paths.base`](configuration#paths) to match your repo name. This is because the site will be served from `https://your-username.github.io/your-repo-name` rather than from the root. +When building for [GitHub Pages](https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages), if your repo name is not equivalent to `your-username.github.io`, make sure to update [`config.paths.base`](configuration#paths) to match your repo name. This is because the site will be served from `https://your-username.github.io/your-repo-name` rather than from the root. You'll also want to generate a fallback `404.html` page to replace the default 404 page shown by GitHub Pages. @@ -104,22 +106,24 @@ A config for GitHub Pages might look like the following: ```js // @errors: 2307 2322 -/// file: svelte.config.js +/// file: vite.config.js +import process from 'node:process'; import adapter from '@sveltejs/adapter-static'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter({ - fallback: '404.html' - }), - paths: { - base: process.argv.includes('dev') ? '' : process.env.BASE_PATH - } - } -}; - -export default config; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + adapter: adapter({ + fallback: '404.html' + }), + paths: { + base: process.argv.includes('dev') ? '' : process.env.BASE_PATH + }, + }) + ] +}); ``` You can use GitHub actions to automatically deploy your site to GitHub Pages when you make a change. Here's an example workflow: diff --git a/documentation/docs/25-build-and-deploy/55-single-page-apps.md b/documentation/docs/25-build-and-deploy/55-single-page-apps.md index 646f54eaf666..5314dda869f8 100644 --- a/documentation/docs/25-build-and-deploy/55-single-page-apps.md +++ b/documentation/docs/25-build-and-deploy/55-single-page-apps.md @@ -16,23 +16,24 @@ First, disable SSR for the pages you don't want to prerender. These pages will b export const ssr = false; ``` -If you don't have any server-side logic (i.e. `+page.server.js`, `+layout.server.js` or `+server.js` files) you can use [`adapter-static`](adapter-static) to create your SPA. Install `adapter-static` with `npm i -D @sveltejs/adapter-static` and add it to your `svelte.config.js` with the `fallback` option: +If you don't have any server-side logic (i.e. `+page.server.js`, `+layout.server.js` or `+server.js` files) you can use [`adapter-static`](adapter-static) to create your SPA. Install `adapter-static` with `npm i -D @sveltejs/adapter-static` and add it to your `vite.config.js` with the `fallback` option: ```js // @errors: 2307 -/// file: svelte.config.js +/// file: vite.config.js import adapter from '@sveltejs/adapter-static'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter({ - fallback: '200.html' // may differ from host to host +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + adapter: adapter({ + fallback: '200.html' // may differ from host to host + }) }) - } -}; - -export default config; + ] +}); ``` The `fallback` page is an HTML page created by SvelteKit from your page template (e.g. `app.html`) that loads your app and navigates to the correct route. For example [Surge](https://surge.sh/help/adding-a-200-page-for-client-side-routing), a static web host, lets you add a `200.html` file that will handle any requests that don't correspond to static assets or prerendered pages. diff --git a/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md b/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md index a1617fdffbca..a7f16b855eae 100644 --- a/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md +++ b/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md @@ -14,34 +14,35 @@ This adapter will be installed by default when you use [`adapter-auto`](adapter- ## Usage -Install with `npm i -D @sveltejs/adapter-cloudflare`, then add the adapter to your `svelte.config.js`: +Install with `npm i -D @sveltejs/adapter-cloudflare`, then add the adapter to your `vite.config.js`: ```js // @errors: 2307 -/// file: svelte.config.js +/// file: vite.config.js import adapter from '@sveltejs/adapter-cloudflare'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter({ - // See below for an explanation of these options - config: undefined, - platformProxy: { - configPath: undefined, - environment: undefined, - persist: undefined - }, - fallback: 'plaintext', - routes: { - include: ['/*'], - exclude: [''] - } +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + adapter: adapter({ + // See below for an explanation of these options + config: undefined, + platformProxy: { + configPath: undefined, + environment: undefined, + persist: undefined + }, + fallback: 'plaintext', + routes: { + include: ['/*'], + exclude: [''] + } + }) }) - } -}; - -export default config; + ] +}); ``` ## Options @@ -154,21 +155,27 @@ export async function POST({ request, platform }) { } ``` -> [!NOTE] SvelteKit's built-in [`$env` module]($env-static-private) should be preferred for environment variables. +> [!NOTE] SvelteKit's built-in [`$app/env/*` modules](environment-variables) should be preferred for environment variables. -To make these types available to your app, install [`@cloudflare/workers-types`](https://www.npmjs.com/package/@cloudflare/workers-types) and reference them in your `src/app.d.ts`: +To make these types available to your app, install [`wrangler`](https://www.npmjs.com/package/wrangler), run [`wrangler types`](https://developers.cloudflare.com/workers/languages/typescript/), and reference them in your `src/app.d.ts`: ```ts +// @filename: ambient.d.ts +import { KVNamespace, DurableObjectNamespace } from '@cloudflare/workers-types'; + +namespace Cloudflare { + export interface Env { + YOUR_KV_NAMESPACE: KVNamespace; + YOUR_DURABLE_OBJECT_NAMESPACE: DurableObjectNamespace; + } +} /// file: src/app.d.ts -+++import { KVNamespace, DurableObjectNamespace } from '@cloudflare/workers-types';+++ +// ---cut--- declare global { namespace App { interface Platform { -+++ env: { - YOUR_KV_NAMESPACE: KVNamespace; - YOUR_DURABLE_OBJECT_NAMESPACE: DurableObjectNamespace; - };+++ ++++ env: Cloudflare.Env;+++ } } } @@ -180,6 +187,8 @@ export {}; Cloudflare specific values in the `platform` property are emulated during dev and preview modes. Local [bindings](https://developers.cloudflare.com/workers/wrangler/configuration/#bindings) are created based on your [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/) and are used to populate `platform.env` during development and preview. Use the adapter config [`platformProxy` option](#Options-platformProxy) to change your preferences for the bindings. +> [!NOTE] [Durable Objects](https://developers.cloudflare.com/durable-objects/) and [Workflows](https://developers.cloudflare.com/workflows/), which require custom classes to be exported from your worker, are not currently supported. + For testing the build, you should use [Wrangler](https://developers.cloudflare.com/workers/wrangler/) version 4. Once you have built your site, run `wrangler dev .svelte-kit/cloudflare/_worker.js` if you're testing for Cloudflare Workers or `wrangler pages dev .svelte-kit/cloudflare` for Cloudflare Pages. ## Headers and redirects @@ -217,22 +226,20 @@ Alternatively, you can [prerender](page-options#prerender) the routes in questio Cloudflare no longer recommends using [Workers Sites](https://developers.cloudflare.com/workers/configuration/sites/configuration/) and instead recommends using [Workers Static Assets](https://developers.cloudflare.com/workers/static-assets/). To migrate, replace `@sveltejs/adapter-cloudflare-workers` with `@sveltejs/adapter-cloudflare` and remove all `site` configuration settings from your Wrangler configuration file, then add the `assets.directory` and `assets.binding` configuration settings: -### svelte.config.js - ```js // @errors: 2307 -/// file: svelte.config.js ----import adapter from '@sveltejs/adapter-cloudflare-workers';--- +/// file: vite.config.js +++import adapter from '@sveltejs/adapter-cloudflare';+++ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter() - } -}; - -export default config; +export default defineConfig({ + plugins: [ + sveltekit({ + +++adapter: adapter()+++ + }) + ] +}); ``` ### wrangler.toml diff --git a/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md b/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md index 418a7e198adf..ab0dd1192b58 100644 --- a/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md +++ b/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md @@ -8,23 +8,24 @@ To deploy to [Cloudflare Workers](https://workers.cloudflare.com/) with [Workers ## Usage -Install with `npm i -D @sveltejs/adapter-cloudflare-workers`, then add the adapter to your `svelte.config.js`: +Install with `npm i -D @sveltejs/adapter-cloudflare-workers`, then add the adapter to your `vite.config.js`: ```js // @errors: 2307 -/// file: svelte.config.js +/// file: vite.config.js +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; import adapter from '@sveltejs/adapter-cloudflare-workers'; -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter({ - // see below for options that can be set here +export default defineConfig({ + plugins: [ + sveltekit({ ++++ adapter: adapter({ + // see below for options that can be set here + })+++ }) - } -}; - -export default config; + ] +}); ``` ## Options @@ -105,7 +106,7 @@ export async function POST({ request, platform }) { } ``` -> [!NOTE] SvelteKit's built-in [`$env` module]($env-static-private) should be preferred for environment variables. +> [!NOTE] SvelteKit's built-in [`$app/env/*` modules](environment-variables) should be preferred for environment variables. To make these types available to your app, install [`@cloudflare/workers-types`](https://www.npmjs.com/package/@cloudflare/workers-types) and reference them in your `src/app.d.ts`: diff --git a/documentation/docs/25-build-and-deploy/80-adapter-netlify.md b/documentation/docs/25-build-and-deploy/80-adapter-netlify.md index 9316eaf7af7d..bdee4e4641a9 100644 --- a/documentation/docs/25-build-and-deploy/80-adapter-netlify.md +++ b/documentation/docs/25-build-and-deploy/80-adapter-netlify.md @@ -8,31 +8,26 @@ This adapter will be installed by default when you use [`adapter-auto`](adapter- ## Usage -Install with `npm i -D @sveltejs/adapter-netlify`, then add the adapter to your `svelte.config.js`: +Install with `npm i -D @sveltejs/adapter-netlify`, then add the adapter to your `vite.config.js`: ```js // @errors: 2307 -/// file: svelte.config.js +/// file: vite.config.js import adapter from '@sveltejs/adapter-netlify'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - // default options are shown - adapter: adapter({ - // if true, will create a Netlify Edge Function rather - // than using standard Node-based functions - edge: false, - - // if true, will split your app into multiple functions - // instead of creating a single one for the entire app. - // if `edge` is true, this option cannot be used - split: false +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + adapter: adapter({ + // See below for an explanation of these options + edge: false, + split: false + }) }) - } -}; - -export default config; + ] +}); ``` Then, make sure you have a [netlify.toml](https://docs.netlify.com/configure-builds/file-based-configuration) file in the project root. This will determine where to write static assets based on the `build.publish` settings, as per this sample configuration: @@ -43,34 +38,21 @@ Then, make sure you have a [netlify.toml](https://docs.netlify.com/configure-bui publish = "build" ``` -If the `netlify.toml` file or the `build.publish` value is missing, a default value of `"build"` will be used. Note that if you have set the publish directory in the Netlify UI to something else then you will need to set it in `netlify.toml` too, or use the default value of `"build"`. +If the `netlify.toml` file or the `build.publish` value is missing, a default value of `"build"` will be used. -### Node version +> [!NOTE] If you have set the publish directory in the Netlify UI to something else then you will need to set it in `netlify.toml` too. -New projects will use the current Node LTS version by default. However, if you're upgrading a project you created a while ago it may be stuck on an older version. See [the Netlify docs](https://docs.netlify.com/configure-builds/manage-dependencies/#node-js-and-javascript) for details on manually specifying a current Node version. +## Options -## Netlify Edge Functions +### `edge` -SvelteKit supports [Netlify Edge Functions](https://docs.netlify.com/build/edge-functions/overview/). If you pass the option `edge: true` to the `adapter` function, server-side rendering will happen in a Deno-based edge function that's deployed close to the site visitor. If set to `false` (the default), the site will deploy to Node-based Netlify Functions. +If `true`, your app will be deployed as a [Netlify Edge Function](https://docs.netlify.com/build/edge-functions/overview/) rather than the standard Node-based function. -```js -// @errors: 2307 -/// file: svelte.config.js -import adapter from '@sveltejs/adapter-netlify'; +### `split` -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter({ - // will create a Netlify Edge Function using Deno-based - // rather than using standard Node-based functions - edge: true - }) - } -}; +If `true`, your app will be split into multiple functions instead of a single one for the entire app. -export default config; -``` +If `edge` is `true`, this option cannot be used. ## Netlify alternatives to SvelteKit functionality @@ -83,7 +65,7 @@ The [`_headers`](https://docs.netlify.com/routing/headers/#syntax-for-the-header ### Netlify Forms 1. Create your Netlify HTML form as described [here](https://docs.netlify.com/forms/setup/#html-forms), e.g. as `/routes/contact/+page.svelte`. (Don't forget to add the hidden `form-name` input element!) -2. Netlify's build bot parses your HTML files at deploy time, which means your form must be [prerendered](page-options#prerender) as HTML. You can either add `export const prerender = true` to your `contact.svelte` to prerender just that page or set the `kit.prerender.force: true` option to prerender all pages. +2. Netlify's build bot parses your HTML files at deploy time, which means your form must be [prerendered](page-options#prerender) as HTML. You can either add `export const prerender = true` to your `contact.svelte` to prerender just that page or set the `prerender.force: true` option to prerender all pages. 3. If your Netlify form has a [custom success message](https://docs.netlify.com/forms/setup/#success-messages) like `` then ensure the corresponding `/routes/success/+page.svelte` exists and is prerendered. ### Netlify Functions diff --git a/documentation/docs/25-build-and-deploy/90-adapter-vercel.md b/documentation/docs/25-build-and-deploy/90-adapter-vercel.md index 50f25f4a9299..eff04f2c9cfb 100644 --- a/documentation/docs/25-build-and-deploy/90-adapter-vercel.md +++ b/documentation/docs/25-build-and-deploy/90-adapter-vercel.md @@ -8,22 +8,24 @@ This adapter will be installed by default when you use [`adapter-auto`](adapter- ## Usage -Install with `npm i -D @sveltejs/adapter-vercel`, then add the adapter to your `svelte.config.js`: +Install with `npm i -D @sveltejs/adapter-vercel`, then add the adapter to your `vite.config.js`: ```js -/// file: svelte.config.js +// @errors: 2554 +/// file: vite.config.js import adapter from '@sveltejs/adapter-vercel'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter({ - // see below for options that can be set here +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + adapter: adapter({ + // see below for options that can be set here + }) }) - } -}; - -export default config; + ] +}); ``` ## Deployment configuration @@ -40,17 +42,11 @@ export const config = { }; ``` -The following options apply to all functions: +You can set the following options: -- `runtime`: `'edge'`, `'nodejs20.x'` or `'nodejs22.x'`. By default, the adapter will select the `'nodejs.x'` corresponding to the Node version your project is configured to use on the Vercel dashboard - > [!NOTE] This option is deprecated and will be removed in a future version, at which point all your functions will use whichever Node version is specified in the project configuration on Vercel -- `regions`: an array of [edge network regions](https://vercel.com/docs/concepts/edge-network/regions) (defaulting to `["iad1"]` for serverless functions) or `'all'` if `runtime` is `edge` (its default). Note that multiple regions for serverless functions are only supported on Enterprise plans +- `runtime`: `'nodejs22.x'`, `'nodejs24.x'` or `bun1.x`. By default, the adapter will select the runtime used for the build, which corresponds to the Node version your project is configured to use on the Vercel dashboard unless you [build the app with Bun](https://bun.com/docs/guides/ecosystem/vite) +- `regions`: an array of [edge network regions](https://vercel.com/docs/concepts/edge-network/regions) (defaulting to `["iad1"]`). Note that multiple regions for serverless functions are only supported on Enterprise plans - `split`: if `true`, causes a route to be deployed as an individual function. If `split` is set to `true` at the adapter level, all routes will be deployed as individual functions - -Additionally, the following option applies to edge functions: -- `external`: an array of dependencies that esbuild should treat as external when bundling functions. This should only be used to exclude optional dependencies that will not run outside Node - -And the following option apply to serverless functions: - `memory`: the amount of memory available to the function. Defaults to `1024` Mb, and can be decreased to `128` Mb or [increased](https://vercel.com/docs/concepts/limits/overview#serverless-function-memory) in 64Mb increments up to `3008` Mb on Pro or Enterprise accounts - `maxDuration`: [maximum execution duration](https://vercel.com/docs/functions/runtimes#max-duration) of the function. Defaults to `10` seconds for Hobby accounts, `15` for Pro and `900` for Enterprise - `isr`: configuration Incremental Static Regeneration, described below @@ -64,24 +60,26 @@ If your functions need to access data in a specific region, it's recommended tha You may set the `images` config to control how Vercel builds your images. See the [image configuration reference](https://vercel.com/docs/build-output-api/v3/configuration#images) for full details. As an example, you may set: ```js -/// file: svelte.config.js +// @errors: 2554 +/// file: vite.config.js import adapter from '@sveltejs/adapter-vercel'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter({ - images: { - sizes: [640, 828, 1200, 1920, 3840], - formats: ['image/avif', 'image/webp'], - minimumCacheTTL: 300, - domains: ['example-app.vercel.app'], - } +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + adapter: adapter({ + images: { + sizes: [640, 828, 1200, 1920, 3840], + formats: ['image/avif', 'image/webp'], + minimumCacheTTL: 300, + domains: ['example-app.vercel.app'], + } + }) }) - } -}; - -export default config; + ] +}); ``` ## Incremental Static Regeneration @@ -93,7 +91,13 @@ Vercel supports [Incremental Static Regeneration](https://vercel.com/docs/increm To add ISR to a route, include the `isr` property in your `config` object: ```js -import { BYPASS_TOKEN } from '$env/static/private'; +// @filename: env.d.ts +declare module '$app/env/private' { + export const BYPASS_TOKEN: string; +} +// @filename: +page.server.js +// ---cut--- +import { BYPASS_TOKEN } from '$app/env/private'; /** @type {import('@sveltejs/adapter-vercel').Config} */ export const config = { @@ -141,11 +145,17 @@ A list of valid query parameters that contribute to the cache key. Other paramet ## Environment variables -Vercel makes a set of [deployment-specific environment variables](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) available. Like other environment variables, these are accessible from `$env/static/private` and `$env/dynamic/private` (sometimes — more on that later), and inaccessible from their public counterparts. To access one of these variables from the client: +Vercel makes a set of [deployment-specific environment variables](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) available. Like other environment variables, these are accessible from `$app/env/private` if explicitly defined in `src/env.ts`. To access one of these variables from the client: ```js /// file: +layout.server.js -import { VERCEL_COMMIT_REF } from '$env/static/private'; +// @filename: env.d.ts +declare module '$app/env/private' { + export const VERCEL_COMMIT_REF: string; +} +// @filename: +layout.server.js +// ---cut--- +import { VERCEL_COMMIT_REF } from '$app/env/private'; /** @type {import('./$types').LayoutServerLoad} */ export function load() { @@ -165,7 +175,7 @@ export function load() {

This staging environment was deployed from {data.deploymentGitBranch}.

``` -Since all of these variables are unchanged between build time and run time when building on Vercel, we recommend using `$env/static/private` — which will statically replace the variables, enabling optimisations like dead code elimination — rather than `$env/dynamic/private`. +Since all of these variables are unchanged between build time and run time when building on Vercel, we recommend configuring the variable with `static: true` — which will statically replace the variables, enabling optimisations like dead code elimination. ## Skew protection @@ -193,12 +203,6 @@ Projects created before a certain date may default to using an older Node versio ### Accessing the file system -You can't use `fs` in edge functions. - -You _can_ use it in serverless functions, but it won't work as expected, since files are not copied from your project into your deployment. Instead, use the [`read`]($app-server#read) function from `$app/server` to access your files. It also works inside routes deployed as edge functions by fetching the file from the deployed public assets location. +Using `node:fs` directly in serverless functions most likely won't work as you expect, since files are not copied from your project into your deployment. Instead, use the [`read`]($app-server#read) function from `$app/server` to access your files. Alternatively, you can [prerender](page-options#prerender) the routes in question. - -### Deployment protection - -If using [`read`]($app-server#read) in an edge function, SvelteKit will `fetch` the file in question from your deployment. If you are using [Deployment Protection](https://vercel.com/docs/deployment-protection), you must also enable [Protection Bypass for Automation](https://vercel.com/docs/deployment-protection/methods-to-bypass-deployment-protection/protection-bypass-automation) so that the request does not result in a [401 Unauthorized](https://http.dog/401) response. diff --git a/documentation/docs/25-build-and-deploy/99-writing-adapters.md b/documentation/docs/25-build-and-deploy/99-writing-adapters.md index 931420b8fb8f..a4002696a018 100644 --- a/documentation/docs/25-build-and-deploy/99-writing-adapters.md +++ b/documentation/docs/25-build-and-deploy/99-writing-adapters.md @@ -39,6 +39,13 @@ export default function (options) { // Return `true` if this adapter supports loading `instrumentation.server.js`. // Return `false if it can't, or throw a descriptive error. } + }, + vite: { + plugins: { + // add plugins here to integrate with Vite + pre: [], + post: [] + } } }; @@ -46,7 +53,7 @@ export default function (options) { } ``` -Of these, `name` and `adapt` are required. `emulate` and `supports` are optional. +Of these, `name` and `adapt` are required. `emulate`, `vite`, and `supports` are optional. Within the `adapt` method, there are a number of things that an adapter should do: @@ -57,7 +64,6 @@ Within the `adapt` method, there are a number of things that an adapter should d - Instantiates the app with a manifest generated with `builder.generateManifest({ relativePath })` - Listens for requests from the platform, converts them to a standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) if necessary, calls the `server.respond(request, { getClientAddress })` function to generate a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) and responds with it - expose any platform-specific information to SvelteKit via the `platform` option passed to `server.respond` - - Globally shims `fetch` to work on the target platform, if necessary. SvelteKit provides a `@sveltejs/kit/node/polyfills` helper for platforms that can use `undici` - Bundle the output to avoid needing to install dependencies on the target platform, if necessary - Put the user's static files and the generated JS/CSS in the correct location for the target platform diff --git a/documentation/docs/30-advanced/10-advanced-routing.md b/documentation/docs/30-advanced/10-advanced-routing.md index 8b5ddba0a7e1..22656e8aa3bf 100644 --- a/documentation/docs/30-advanced/10-advanced-routing.md +++ b/documentation/docs/30-advanced/10-advanced-routing.md @@ -61,7 +61,7 @@ export function load(event) { } ``` -> [!NOTE] If you don't handle 404 cases, they will appear in [`handleError`](hooks#handleError) +> [!NOTE] If you don't handle 404 cases, they will appear in [`handleError`](hooks#handleError) as [framework errors](errors#Framework-errors), with `kind: 'framework'`. Otherwise, they will appear with `kind: 'app'`. ## Optional parameters @@ -71,21 +71,18 @@ Note that an optional route parameter cannot follow a rest parameter (`[...rest] ## Matching -A route like `src/routes/fruits/[page]` would match `/fruits/apple`, but it would also match `/fruits/rocketship`. We don't want that. You can ensure that route parameters are well-formed by adding a _matcher_ — which takes the parameter string (`"apple"` or `"rocketship"`) and returns `true` if it is valid — to your `src/params` directory... +A route like `src/routes/fruits/[page]` would match `/fruits/apple`, but it would also match `/fruits/rocketship`. We don't want that. You can ensure that route parameters are well-formed by adding a _matcher_ to your `src/params.js` file (or `src/params.ts`)... ```js -// TODO: remove the ts error once kit 3 docs are main -// ---cut--- -// @errors: 1360 -/// file: src/params/fruit.js -/** - * @param {string} param - * @return {param is ('apple' | 'orange')} - * @satisfies {import('@sveltejs/kit').ParamMatcher} - */ -export function match(param) { - return param === 'apple' || param === 'orange'; -} +/// file: src/params.js +import { defineParams } from '@sveltejs/kit/params'; + +export const params = defineParams({ + fruit: (param) => { + if (param !== 'apple' && param !== 'orange') return; + return param; + } +}); ``` ...and augmenting your routes: @@ -94,12 +91,42 @@ export function match(param) { src/routes/fruits/[page+++=fruit+++] ``` -If the pathname doesn't match, SvelteKit will try to match other routes (using the sort order specified below), before eventually returning a 404. +If the pathname doesn't match, SvelteKit will try to match other routes (using the sort order specified below), before eventually returning a 404. If it does match, the returned value is passed as the param value. + +You can also use a [Standard Schema](https://standardschema.dev) — for example with [Valibot](https://valibot.dev): + +```js +/// file: src/params.js +import { defineParams } from '@sveltejs/kit/params'; +import * as v from 'valibot'; + +export const params = defineParams({ + number: v.pipe(v.string(), v.toNumber()) +}); +``` + +If validation fails, the route does not match. If it succeeds, the param is typed with the output type of the schema, which must extend `string`, `boolean`, `number` (as in the example above) or `bigint`: + +```js +/// file: src/routes/items/[id=number]/+page.js +/** @type {import('./$types').PageLoad} */ +export function load({ params }) { + console.log(typeof params.id); // 'number' +} +``` + +Transformations should be _symmetrical_ — if converting to a number, then calling `toString()` on that number should return the original string. This allows the [`resolve`]($app-paths#resolve) function to construct a pathname correctly: -Each module in the `params` directory corresponds to a matcher, with the exception of `*.test.js` and `*.spec.js` files which may be used to unit test your matchers. +```js +import { resolve } from '$app/paths'; + +resolve('/blog/[id=number]', { id: 1 }); +``` > [!NOTE] Matchers run both on the server and in the browser. +> [!NOTE] Prior to SvelteKit 3, you had to define each param matcher in a separate file, all listed under a `params` folder (for example `src/params/foo.js` with `export const match = (param) => param === 'foo';`), and matching was determined by whether or not the matcher returns a truthy value (which means no value transformation took place). + ## Sorting It's possible for multiple routes to match a given path. For example each of these routes would match `/foo-abc`: @@ -262,7 +289,7 @@ Not all use cases are suited for layout grouping, nor should you feel compelled ```svelte @@ -274,12 +301,12 @@ Not all use cases are suited for layout grouping, nor should you feel compelled ```js /// file: src/routes/nested/route/+layout.js // @filename: ambient.d.ts -declare module "$lib/reusable-load-function" { +declare module "#lib/reusable-load-function.js" { export function reusableLoad(event: import('@sveltejs/kit').LoadEvent): Promise>; } // @filename: index.js // ---cut--- -import { reusableLoad } from '$lib/reusable-load-function'; +import { reusableLoad } from '#lib/reusable-load-function.js'; /** @type {import('./$types').PageLoad} */ export function load(event) { diff --git a/documentation/docs/30-advanced/20-hooks.md b/documentation/docs/30-advanced/20-hooks.md index a157dd699d6e..70ea50d824b2 100644 --- a/documentation/docs/30-advanced/20-hooks.md +++ b/documentation/docs/30-advanced/20-hooks.md @@ -20,7 +20,7 @@ This function runs every time the SvelteKit server receives a [request](web-stan ```js /// file: src/hooks.server.js -/** @type {import('@sveltejs/kit').Handle} */ +/** @type {import('@sveltejs/kit/hooks').Handle} */ export async function handle({ event, resolve }) { if (event.url.pathname.startsWith('/custom')) { return new Response('custom response'); @@ -49,7 +49,7 @@ You can define multiple `handle` functions and execute them with the [`sequence` ```js /// file: src/hooks.server.js -/** @type {import('@sveltejs/kit').Handle} */ +/** @type {import('@sveltejs/kit/hooks').Handle} */ export async function handle({ event, resolve }) { const response = await resolve(event, { transformPageChunk: ({ html }) => html.replace('old', 'new'), @@ -84,7 +84,7 @@ const getUserInformation: (cookie: string | void) => Promise; // @filename: index.js // ---cut--- -/** @type {import('@sveltejs/kit').Handle} */ +/** @type {import('@sveltejs/kit/hooks').Handle} */ export async function handle({ event, resolve }) { event.locals.user = await getUserInformation(event.cookies.get('sessionid')); @@ -112,7 +112,7 @@ For example, your `load` function might make a request to a public URL like `htt ```js /// file: src/hooks.server.js -/** @type {import('@sveltejs/kit').HandleFetch} */ +/** @type {import('@sveltejs/kit/hooks').HandleFetch} */ export async function handleFetch({ request, fetch }) { if (request.url.startsWith('https://api.yourapp.com/')) { // clone the original request, but change the URL @@ -133,7 +133,7 @@ There is one caveat: if your app and your API are on sibling subdomains — `www ```js /// file: src/hooks.server.js // @errors: 2345 -/** @type {import('@sveltejs/kit').HandleFetch} */ +/** @type {import('@sveltejs/kit/hooks').HandleFetch} */ export async function handleFetch({ event, request, fetch }) { if (request.url.startsWith('https://api.my-domain.com/')) { request.headers.set('cookie', event.request.headers.get('cookie')); @@ -143,59 +143,46 @@ export async function handleFetch({ event, request, fetch }) { } ``` -## handleValidationError -> [!NOTE] Can be added to `src/hooks.server.js` - -This hook is called when a remote function is called with an argument that does not match the provided [Standard Schema](https://standardschema.dev/). It must return an object matching the shape of [`App.Error`](types#Error). -Say you have a remote function that expects a string as its argument ... - -```js -/// file: todos.remote.js -import * as v from 'valibot'; -import { query } from '$app/server'; - -export const getTodo = query(v.string(), (id) => { - // implementation... -}); -``` +## handleError -...but it is called with something that doesn't match the schema — such as a number (e.g. `await getTodos(1)`) — then validation will fail, the server will respond with a [400 status code](https://http.dog/400), and the function will throw with the message 'Bad Request'. +> [!NOTE] Can be added to `src/hooks.server.js` and `src/hooks.client.js` -To customise this message and add additional properties to the error object, implement `handleValidationError`: +This function is called for every error thrown while loading, rendering, or responding to a request. This allows for two things: -```js -/// file: src/hooks.server.js -/** @type {import('@sveltejs/kit').HandleValidationError} */ -export function handleValidationError({ issues }) { - return { - message: 'No thank you' - }; -} -``` +- you can log the error +- you can generate a custom representation of the error that is safe to show to users, omitting sensitive details like messages and stack traces -Be thoughtful about what information you expose here, as the most likely reason for validation to fail is that someone is sending malicious requests to your server. +Alongside the `event`, the hook receives a `kind` discriminant that tells you where the error came from, and the `error` itself: -## handleError +- `'app'` — the error came from your app via [`error(...)`](@sveltejs-kit#error) + - `error` is the error body, which matches [`App.Error`](types#Error) + - defaults to the error body itself +- `'framework'` — the error came from SvelteKit, such as a 404, 405 or 413 + - `error` is `{ status, message }`, where `message` is safe text like `Not Found` + - defaults to that same `{ status, message }` +- `'validation'` (server only) — the error came from validating a remote function argument against its [Standard Schema](https://standardschema.dev/) + - `error` is `{ status: 400, message: 'Bad Request' }`, and `issues` contains the validation issues + - defaults to the `error` object; the issues are not exposed unless you explicitly return them + - to access validation-library-specific issue properties, parameterise `HandleServerError` with the issue type, for example `HandleServerError` +- `'unknown'` — we don't know what went wrong; the error was thrown by your code, or code it calls + - `error` is the thrown value, which may contain information unsafe to expose + - defaults to `{ status: 500, message: 'Internal Error' }` -> [!NOTE] Can be added to `src/hooks.server.js` and `src/hooks.client.js` +The next section, [Errors](errors), explains the other categories in more detail. Redirects are not errors, and never reach the hook. -If an [unexpected error](errors#Unexpected-errors) is thrown during loading, rendering, or from an endpoint, this function will be called with the `error`, `event`, `status` code and `message`. This allows for two things: +The hook returns an object matching [`App.Error`](types#Error), in which `status` and `message` are optional — return them only to override the defaults in the list above. -- you can log the error -- you can generate a custom representation of the error that is safe to show to users, omitting sensitive details like messages and stack traces. The returned value, which defaults to `{ message }`, becomes the value of `page.error`. +> [!NOTE] If you augment `App.Error` with additional _required_ properties, the hook must return them. -For errors thrown from your code (or library code called by your code) the status will be 500 and the message will be "Internal Error". While `error.message` may contain sensitive information that should not be exposed to users, `message` is safe (albeit meaningless to the average user). - -To add more information to the `page.error` object in a type-safe way, you can customize the expected shape by declaring an `App.Error` interface (which must include `message: string`, to guarantee sensible fallback behavior). This allows you to — for example — append a tracking ID for users to quote in correspondence with your technical support staff: +To add more information to the `page.error` object in a type-safe way, augment the existing `App.Error` interface with your additional properties. The built-in `status` and `message` properties are already present and do not need to be redeclared. For example, you can add a tracking ID for users to quote when contacting support: ```ts /// file: src/app.d.ts declare global { namespace App { interface Error { - message: string; errorId: string; } } @@ -219,15 +206,29 @@ import * as Sentry from '@sentry/sveltekit'; Sentry.init({/*...*/}) -/** @type {import('@sveltejs/kit').HandleServerError} */ -export async function handleError({ error, event, status, message }) { +/** @type {import('@sveltejs/kit/hooks').HandleServerError} */ +export async function handleError({ kind, error, event }) { + if (kind === 'app') { + // you created this error with `error(...)`, so it already + // matches `App.Error` — pass it through unchanged + return error; + } + const errorId = crypto.randomUUID(); + if (kind === 'framework') { + // a 404 (or similar) — `error.status` and `error.message` are safe to + // expose, so we keep them and just add our own property + return { ...error, errorId }; + } + // example integration with https://sentry.io/ Sentry.captureException(error, { - extra: { event, errorId, status } + extra: { event, errorId } }); + // `status` and `message` are optional — we only override `message`, + // so the status stays at its default of 500 return { message: 'Whoops!', errorId @@ -250,13 +251,21 @@ import * as Sentry from '@sentry/sveltekit'; Sentry.init({/*...*/}) -/** @type {import('@sveltejs/kit').HandleClientError} */ -export async function handleError({ error, event, status, message }) { +/** @type {import('@sveltejs/kit/hooks').HandleClientError} */ +export async function handleError({ kind, error, event }) { + if (kind === 'app') { + return error; + } + const errorId = crypto.randomUUID(); + if (kind === 'framework') { + return { ...error, errorId }; + } + // example integration with https://sentry.io/ Sentry.captureException(error, { - extra: { event, errorId, status } + extra: { event, errorId } }); return { @@ -268,7 +277,7 @@ export async function handleError({ error, event, status, message }) { > [!NOTE] In `src/hooks.client.js`, the type of `handleError` is `HandleClientError` instead of `HandleServerError`, and `event` is a `NavigationEvent` rather than a `RequestEvent`. -This function is not called for _expected_ errors (those thrown with the [`error`](@sveltejs-kit#error) function imported from `@sveltejs/kit`). +Errors that were already transformed by the server-side hook are not passed to the client-side hook a second time. During development, if an error occurs because of a syntax error in your Svelte code, the passed in error has a `frame` property appended highlighting the location of the error. @@ -285,9 +294,9 @@ This function runs once, when the server is created or the app starts in the bro ```js // @errors: 2307 /// file: src/hooks.server.js -import * as db from '$lib/server/database'; +import * as db from '#lib/server/database.js'; -/** @type {import('@sveltejs/kit').ServerInit} */ +/** @type {import('@sveltejs/kit/hooks').ServerInit} */ export async function init() { await db.connect(); } @@ -315,7 +324,7 @@ const translated = { '/fr/a-propos': '/fr/about', }; -/** @type {import('@sveltejs/kit').Reroute} */ +/** @type {import('@sveltejs/kit/hooks').Reroute} */ export function reroute({ url }) { if (url.pathname in translated) { return translated[url.pathname]; @@ -333,7 +342,7 @@ Since version 2.18, the `reroute` hook can be asynchronous, allowing it to (for // @errors: 2345 2304 /// file: src/hooks.js -/** @type {import('@sveltejs/kit').Reroute} */ +/** @type {import('@sveltejs/kit/hooks').Reroute} */ export async function reroute({ url, fetch }) { // Ask a special endpoint within your app about the destination if (url.pathname === '/api/reroute') return; @@ -358,9 +367,9 @@ This is a collection of _transporters_, which allow you to pass custom types — ```js // @errors: 2307 /// file: src/hooks.js -import { Vector } from '$lib/math'; +import { Vector } from '#lib/math.js'; -/** @type {import('@sveltejs/kit').Transport} */ +/** @type {import('@sveltejs/kit/hooks').Transport} */ export const transport = { Vector: { encode: (value) => value instanceof Vector && [value.x, value.y], diff --git a/documentation/docs/30-advanced/25-errors.md b/documentation/docs/30-advanced/25-errors.md index 49f826ad1ec0..493b8d7a54fe 100644 --- a/documentation/docs/30-advanced/25-errors.md +++ b/documentation/docs/30-advanced/25-errors.md @@ -6,55 +6,52 @@ Errors are an inevitable fact of software development. SvelteKit handles errors ## Error objects -SvelteKit distinguishes between expected and unexpected errors, both of which are represented as simple `{ message: string }` objects by default. +Every error passes through the [`handleError`](hooks#handleError) hook — which can log it and customise it — before it is rendered. The hook's `kind` property identifies where the error came from: your app (`'app'`), SvelteKit (`'framework'`), validation of a [remote function](remote-functions) argument (`'validation'`) or an unknown source (`'unknown'`). By default, all are represented as simple `{ status: number, message: string }` objects. -You can add additional properties, like a `code` or a tracking `id`, as shown in the examples below. (When using TypeScript this requires you to redefine the `Error` type as described in [type safety](errors#Type-safety)). +You can add additional properties, like a `code` or a tracking `id`, as shown in the examples below. (When using TypeScript this requires you to redefine the `Error` type as described in [Type safety](errors#Type-safety) below). -## Expected errors +## App errors -An _expected_ error is one created with the [`error`](@sveltejs-kit#error) helper imported from `@sveltejs/kit`: +An app error is one thrown from your app code using the [`error`](@sveltejs-kit#error) function imported from `@sveltejs/kit`: ```js /// file: src/routes/blog/[slug]/+page.server.js // @filename: ambient.d.ts -declare module '$lib/server/database' { +declare module '#lib/server/database.js' { export function getPost(slug: string): Promise<{ title: string, content: string } | undefined> } // @filename: index.js // ---cut--- import { error } from '@sveltejs/kit'; -import * as db from '$lib/server/database'; +import * as db from '#lib/server/database.js'; /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { const post = await db.getPost(params.slug); if (!post) { - error(404, { - message: 'Not found' - }); + error(404, 'Not found'); } return { post }; } ``` -This throws an exception that SvelteKit catches, causing it to set the response status code to 404 and render an [`+error.svelte`](routing#error) component, where `page.error` is the object provided as the second argument to `error(...)`. +This throws an exception that SvelteKit catches, causing it to set the response status code to 404 and render an [`+error.svelte`](routing#error) component, where the `error` is an `App.Error` object with the provided `status` and `message`. + +On its way there, the error passes through the [`handleError`](hooks#handleError) hook with `kind: 'app'`. Since the shape of the error is determined by your app, it is considered safe to expose, and the hook can pass it through unchanged. ```svelte -

{page.error.message}

+

{error.message}

``` -> [!LEGACY] -> `$app/state` was added in SvelteKit 2.12. If you're using an earlier version or are using Svelte 4, use `$app/stores` instead. - -You can add extra properties to the error object if needed... +You can add extra properties to the error object if needed: ```js // @filename: ambient.d.ts @@ -71,79 +68,74 @@ export {} // @filename: index.js import { error } from '@sveltejs/kit'; // ---cut--- -error(404, { - message: 'Not found', +error(404, 'Not found', { +++code: 'NOT_FOUND'+++ }); ``` -...otherwise, for convenience, you can pass a string as the second argument: +> [!NOTE] [In SvelteKit 1.x](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you) you had to `throw` the `error` yourself -```js -import { error } from '@sveltejs/kit'; -// ---cut--- ----error(404, { message: 'Not found' });--- -+++error(404, 'Not found');+++ -``` +## Framework errors -> [!NOTE] [In SvelteKit 1.x](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you) you had to `throw` the `error` yourself +Some errors are generated by SvelteKit itself rather than by your code — a request that doesn't match any route (404), a `POST` request to a page without actions (405), a request body that exceeds the size limit (413), and so on. -## Unexpected errors +These also go through `handleError`, with `kind: 'framework'`. The `error` you receive is a `{ status, message }` object whose `message` is a terse but safe description of what went wrong, such as `'Not Found'`, so it can be exposed to users as-is. -An _unexpected_ error is any other exception that occurs while handling a request. Since these can contain sensitive information, unexpected error messages and stack traces are not exposed to users. +If you log errors inside `handleError`, remember that framework errors such as 404s are routine — you will generally want to avoid logging them. -By default, unexpected errors are printed to the console (or, in production, your server logs), while the error that is exposed to the user has a generic shape: +## Validation errors -```json -{ "message": "Internal Error" } -``` +Validation errors occur when a [remote function](remote-functions) is called with invalid data. When these are passed to `handleError`, they are accompanied by an array of `issues`. See [Handling validation errors](remote-functions#Handling-validation-errors) for more details. + +## Unknown errors -Unexpected errors will go through the [`handleError`](hooks#handleError) hook, where you can add your own error handling — for example, sending errors to a reporting service, or returning a custom error object which becomes `page.error`. +An _unknown_ error is any other exception that occurs while handling a request. Since these can contain sensitive information, unknown error messages and stack traces are not exposed to users. -## Rendering errors +By default, unknown errors are printed to the console (or, in production, your server logs), while the error that is exposed to the user has a generic shape: -Ordinarily, if an error happens during server-side rendering (for example inside a component's ` - -

{error.message}

-``` +## Error boundaries -The same applies for other error boundaries you define in your code: +Errors that occur during `load` or rendering (for example inside a component's ` @@ -27,8 +28,45 @@ To do this, export a `snapshot` object with `capture` and `restore` methods from ``` -When you navigate away from this page, the `capture` function is called immediately before the page updates, and the returned value is associated with the current entry in the browser's history stack. If you navigate back, the `restore` function is called with the stored value as soon as the page is updated. +When you navigate away from this page — including via [shallow routing](shallow-routing) — the `capture` function is called immediately before the page updates, and the returned value is associated with the current entry in the browser's history stack. If you navigate back, the `restore` function is called with the stored value as soon as the page is updated. + +Snapshots must have a unique ID in order to survive across component remounts and page reloads. By default, this is generated from the stack trace when `snapshot(...)` is called, but you can also explicitly provide an `id` to (for example) keep snapshots stable across deployments, even if the stack trace differs because of changes to the source code, or to distinguish snapshots created via a shared wrapper function or in multiple instances of the same component: + +```js +import { snapshot } from '$app/navigation'; + +let comment = ''; + +// ---cut--- +snapshot({ + +++id: 'comment',+++ + capture: () => comment, + restore: (value) => (comment = value) +}); +``` + +The optional `reset` callback runs on navigations where there is no captured value to restore, such as when a new history entry is created. -The data must be serializable as JSON so that it can be persisted to `sessionStorage`. This allows the state to be restored when the page is reloaded, or when the user navigates back from a different site. +Captured values are serialized with [devalue](https://github.com/sveltejs/devalue), which handles JSON, objects such as `Date` and `Map`, and custom types specified in your [`transport`](hooks#transport) hook. The serialized data is persisted to `sessionStorage`, which allows the state to be restored when the page is reloaded, or when the user navigates back from a different site. > [!NOTE] Avoid returning very large objects from `capture` — once captured, objects will be retained in memory for the duration of the session, and in extreme cases may be too large to persist to `sessionStorage`. + +## export const snapshot + +> [!LEGACY] +> Previously, snapshots were created by exporting a `snapshot` object with `capture` and `restore` methods from a `+page.svelte` or `+layout.svelte`. This form is deprecated in favour of the `snapshot` helper, which can be called from any component. + +```svelte + + +``` + +Values captured this way are serialized as JSON, and shallow navigations do not capture them. diff --git a/documentation/docs/30-advanced/67-shallow-routing.md b/documentation/docs/30-advanced/67-shallow-routing.md index b69cee744fb1..6af1b3e008b3 100644 --- a/documentation/docs/30-advanced/67-shallow-routing.md +++ b/documentation/docs/30-advanced/67-shallow-routing.md @@ -1,44 +1,121 @@ --- -title: Shallow routing +title: Page state & shallow routing --- -As you navigate around a SvelteKit app, you create _history entries_. Clicking the back and forward buttons traverses through this list of entries, re-running any `load` functions and replacing page components as necessary. +As you navigate around a SvelteKit app, you create _history entries_. Clicking the back and forward buttons traverses through this list of entries, re-running any `load` functions, replacing page components, and updating the scroll position and focused element as necessary. -Sometimes, it's useful to create history entries _without_ navigating. For example, you might want to show a modal dialog that the user can dismiss by navigating back. This is particularly valuable on mobile devices, where swipe gestures are often more natural than interacting directly with the UI. In these cases, a modal that is _not_ associated with a history entry can be a source of frustration, as a user may swipe backwards in an attempt to dismiss it and find themselves on the wrong page. +Sometimes, it's useful to create history entries _without_ performing a full navigation. We call this _shallow routing_. -SvelteKit makes this possible with the [`pushState`]($app-navigation#pushState) and [`replaceState`]($app-navigation#replaceState) functions, which allow you to associate state with a history entry without navigating. For example, to implement a history-driven modal: +For example, you might want to show a modal dialog that the user can dismiss by navigating back. This is particularly valuable on mobile devices, where swipe gestures are often more natural than interacting directly with the UI. In these cases, a modal that is _not_ associated with a history entry can be a source of frustration, as a user may swipe backwards in an attempt to dismiss it and find themselves on the wrong page. + +SvelteKit makes this possible with the [`goto`]($app-navigation#goto) function, which allows you to associate state with a history entry without navigating with the `shallow: true` option: ```svelte + + {#if page.state.showModal} history.back()} /> {/if} ``` +State is globally accessible via the [page object]($app-state#page) as `page.state`. You can make page state type-safe by declaring an [`App.PageState`](types#PageState) interface (usually in `src/app.d.ts`). + The modal can be dismissed by navigating back (unsetting `page.state.showModal`) or by interacting with it in a way that causes the `close` callback to run, which will navigate back programmatically. -## API +You can also update the contents of the browser's URL bar during a shallow navigation: + +```js +import { goto } from '$app/navigation'; +const state = { active: true }; +// ---cut--- +goto('/another/page', { + state, + shallow: true +}); +``` + +> [!NOTE] If the user reloads, they will land on `/another/page`, _not_ the currently rendered page — see [Caveats](#Caveats). + +Regardless of whether you choose to update the visible URL or not, [`beforeNavigate`]($app-navigation#beforeNavigate), [`onNavigate`]($app-navigation#onNavigate) and [`afterNavigate`]($app-navigation#afterNavigate) will run with `navigation.type === 'goto'` and `navigation.shallow === true`. + +Once shallow routing is active, `page.shallow` becomes a `{ url, params, route }` object describing the page that _would_ be rendered if the user were to navigate there (which would happen if, for example, they reloaded the page). `page.url`, `page.params` and `page.route` continue to describe the page that is currently rendered. + +```svelte + + + +

The user-visible URL is {page.shallow?.url.href ?? page.url.href}

+

The actual page you're on is {page.url.href}

-The first argument to `pushState` is the URL, relative to the current URL. To stay on the current URL, use `''`. + +``` -The second argument is the new page state, which can be accessed via the [page object]($app-state#page) as `page.state`. You can make page state type-safe by declaring an [`App.PageState`](types#PageState) interface (usually in `src/app.d.ts`). +A regular `goto` call without `shallow: true`, or a standard link click, exits shallow routing. -To set page state without creating a new history entry, use `replaceState` instead of `pushState`. +Navigating back or forward to a shallow entry restores its `page.state` and `page.shallow`. The rendered page, and therefore `page.url`, is still the one the user was on when `goto` was called. To navigate to the visible URL, call `goto(page.shallow.url)` without `shallow: true`. > [!LEGACY] -> `page.state` from `$app/state` was added in SvelteKit 2.12. If you're using an earlier version or are using Svelte 4, use `$page.state` from `$app/stores` instead. +> In SvelteKit 2 this functionality was achieved using `pushState` and `replaceState`, which are now deprecated. Use `goto` with `shallow: true` instead, and use the `replace` option when replacing the current history entry. + +## Routing options + +By default, the above examples create a new navigation entry in the history stack. If you don't want that, you can replace the existing navigation entry instead: + +```js +import { goto } from '$app/navigation'; +const url = new URL('https://example.com'); +const state = { showModal: true }; +// ---cut--- +goto(url, { + state, + replace: true +}); +``` + +By default, state set with `goto` is not restored after a reload. To change that, use `persistState`: + +```js +import { goto } from '$app/navigation'; +const url = new URL('https://example.com'); +const state = { showModal: true }; +// ---cut--- +goto(url, { + state, + persistState: true +}); +``` + +Shallow navigations preserve the current scroll position and focused element by default. You can opt out of this behavior with `reset: true`: + +```js +import { goto } from '$app/navigation'; +const url = new URL('https://example.com'); +// ---cut--- +goto(url, { + shallow: true, + reset: true +}); +``` + +> [!NOTE] +> `page.state` is only populated after JavaScript loads, which can cause flickering UI. Use it carefully. ## Loading data for a route @@ -49,7 +126,7 @@ For this to work, you need to load the data that the `+page.svelte` expects. A c ```svelte The project logo @@ -39,14 +39,15 @@ npm i -D @sveltejs/enhanced-img Adjust `vite.config.js`: ```js -import { sveltekit } from '@sveltejs/kit/vite'; +/// file: vite.config.js +++import { enhancedImages } from '@sveltejs/enhanced-img';+++ +import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vite'; export default defineConfig({ plugins: [ +++enhancedImages(), // must come before the SvelteKit plugin+++ - sveltekit() + sveltekit({/* ... */}) ] }); ``` diff --git a/documentation/docs/40-best-practices/10-accessibility.md b/documentation/docs/40-best-practices/10-accessibility.md index 4c6b3d8be18e..5e174655d7bc 100644 --- a/documentation/docs/40-best-practices/10-accessibility.md +++ b/documentation/docs/40-best-practices/10-accessibility.md @@ -45,7 +45,7 @@ afterNavigate(() => { }); ``` -You can also programmatically navigate to a different page using the [`goto`]($app-navigation#goto) function. By default, this will have the same client-side routing behavior as clicking on a link. However, `goto` also accepts a `keepFocus` option that will preserve the currently-focused element instead of resetting focus. If you enable this option, make sure the currently-focused element still exists on the page after navigation. If the element no longer exists, the user's focus will be lost, making for a confusing experience for assistive technology users. +You can also programmatically navigate to a different page using the [`goto`]($app-navigation#goto) function. By default, this will have the same client-side routing behavior as clicking on a link. However, `goto` also accepts a `reset: false` option that will preserve the current scroll position and the currently-focused element instead of resetting them. If you use this option, make sure the currently-focused element still exists on the page after navigation. If the element no longer exists, the user's focus will be lost, making for a confusing experience for assistive technology users. ## The "lang" attribute @@ -73,7 +73,7 @@ export function get_lang(event: import('@sveltejs/kit').RequestEvent) { // @filename: hooks.server.js import { get_lang } from './utils'; // ---cut--- -/** @type {import('@sveltejs/kit').Handle} */ +/** @type {import('@sveltejs/kit/hooks').Handle} */ export function handle({ event, resolve }) { return resolve(event, { transformPageChunk: ({ html }) => html.replace('%lang%', get_lang(event)) diff --git a/documentation/docs/40-best-practices/20-seo.md b/documentation/docs/40-best-practices/20-seo.md index c878ba8d9e66..e630daf6b99c 100644 --- a/documentation/docs/40-best-practices/20-seo.md +++ b/documentation/docs/40-best-practices/20-seo.md @@ -56,95 +56,3 @@ export async function GET() { ); } ``` - -### AMP - -An unfortunate reality of modern web development is that it is sometimes necessary to create an [Accelerated Mobile Pages (AMP)](https://amp.dev/) version of your site. In SvelteKit this can be done by setting the [`inlineStyleThreshold`](configuration#inlineStyleThreshold) option... - -```js -/// file: svelte.config.js -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - // since isn't - // allowed, inline all styles - inlineStyleThreshold: Infinity - } -}; - -export default config; -``` - -...disabling `csr` in your root `+layout.js`/`+layout.server.js`... - -```js -/// file: src/routes/+layout.server.js -export const csr = false; -``` - -...adding `amp` to your `app.html` - -```html - -... -``` - -...and transforming the HTML using `transformPageChunk` along with `transform` imported from `@sveltejs/amp`: - -```js -/// file: src/hooks.server.js -import * as amp from '@sveltejs/amp'; - -/** @type {import('@sveltejs/kit').Handle} */ -export async function handle({ event, resolve }) { - let buffer = ''; - return await resolve(event, { - transformPageChunk: ({ html, done }) => { - buffer += html; - if (done) return amp.transform(buffer); - } - }); -} -``` - -To prevent shipping any unused CSS as a result of transforming the page to amp, we can use [`dropcss`](https://www.npmjs.com/package/dropcss): - -```js -// @filename: ambient.d.ts -declare module 'dropcss'; - -// @filename: index.js -// ---cut--- -/// file: src/hooks.server.js -// @errors: 2307 -import * as amp from '@sveltejs/amp'; -import dropcss from 'dropcss'; - -/** @type {import('@sveltejs/kit').Handle} */ -export async function handle({ event, resolve }) { - let buffer = ''; - - return await resolve(event, { - transformPageChunk: ({ html, done }) => { - buffer += html; - - if (done) { - let css = ''; - const markup = amp - .transform(buffer) - .replace('⚡', 'amp') // dropcss can't handle this character - .replace(/`; - }); - - css = dropcss({ css, html: markup }).css; - return markup.replace('', `${css}`); - } - } - }); -} - -``` - -> [!NOTE] It's a good idea to use the `handle` hook to validate the transformed HTML using `amphtml-validator`, but only if you're prerendering pages since it's very slow. diff --git a/documentation/docs/60-appendix/10-faq.md b/documentation/docs/60-appendix/10-faq.md index 8eb4af1f420c..a224bb35e62f 100644 --- a/documentation/docs/60-appendix/10-faq.md +++ b/documentation/docs/60-appendix/10-faq.md @@ -16,7 +16,7 @@ If you'd like to include your application's version number or other information ```ts // @errors: 2732 -/// file: svelte.config.js +/// file: vite.config.js import pkg from './package.json' with { type: 'json' }; ``` @@ -31,7 +31,7 @@ Here are a few things to keep in mind when checking if a library is packaged cor - `main` should be defined if `exports` is not. It should be either a CommonJS or ESM file and adhere to the previous bullet. If a `module` field is defined, it should refer to an ESM file. - Svelte components should be distributed as uncompiled `.svelte` files with any JS in the package written as ESM only. Custom script and style languages, like TypeScript and SCSS, should be preprocessed as vanilla JS and CSS respectively. We recommend using [`svelte-package`](./packaging) for packaging Svelte libraries, which will do this for you. -Libraries work best in the browser with Vite when they distribute an ESM version, especially if they are dependencies of a Svelte component library. You may wish to suggest to library authors that they provide an ESM version. However, CommonJS (CJS) dependencies should work as well since, by default, [`vite-plugin-svelte` will ask Vite to pre-bundle them](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/faq.md#what-is-going-on-with-vite-and-pre-bundling-dependencies) using `esbuild` to convert them to ESM. +Libraries work best in the browser with Vite when they distribute an ESM version, especially if they are dependencies of a Svelte component library. You may wish to suggest to library authors that they provide an ESM version. However, CommonJS (CJS) dependencies should work as well since, by default, [`vite-plugin-svelte` will ask Vite to pre-bundle them](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/faq.md#what-is-going-on-with-vite-and-pre-bundling-dependencies) using `rolldown` to convert them to ESM. If you are still encountering issues we recommend searching both [the Vite issue tracker](https://github.com/vitejs/vite/issues) and the issue tracker of the library in question. Sometimes issues can be worked around by fiddling with the [`optimizeDeps`](https://vitejs.dev/config/#dep-optimization-options) or [`ssr`](https://vitejs.dev/config/#ssr-options) config values though we recommend this as only a short-term workaround in favor of fixing the library in question. @@ -70,7 +70,7 @@ If you need access to the `document` or `window` variables or otherwise need cod ```js /// // ---cut--- -import { browser } from '$app/environment'; +import { browser } from '$app/env'; if (browser) { // client-only code here @@ -115,7 +115,7 @@ Finally, you may also consider using an `{#await}` block: ```svelte + +

current pathname: {page.url.pathname}

+``` + +## `$app/tsconfig` + +Your project's `tsconfig.json` should now extend `$app/tsconfig` rather than `./.svelte-kit/tsconfig.json`. It should also specify `include` and `exclude` arrays, as `$app/tsconfig` does not specify these: + +```json +/// file: tsconfig.json +{ + "extends": "$app/tsconfig", + "include": ["src", "test", "*"], + "exclude": ["src/service-worker"] +} +``` + +Some essential `compilerOptions` (`isolatedModules` and `verbatimModuleSyntax`) are included in `$app/tsconfig`, alongside various options that are strongly recommend but which can be overridden in your own config. + +## `$app/tsconfig/service-worker` + +Your service worker needs to be part of a separate TypeScript project, otherwise the types for things like `fetch` events will be incorrect. To do this, exclude the service worker from your project's root `tsconfig.json`, and add a `src/service-worker/tsconfig.json` that extends `$app/tsconfig/service-worker`: + +```json +/// file: src/service-worker/tsconfig.json +{ + "extends": "$app/tsconfig/service-worker" +} +``` + +## `$env/...` (deprecated) + +The various `$env/...` modules have been deprecated in favour of `$app/env/private` and `$app/env/public` — see [Environment variables](environment-variables) for more details. + +## `$service-worker` (removed) + +The `$service-worker` module has been removed. Import `version` from [`$app/env`]($app-env), `assets`, `immutable` and `prerendered` from [`$app/manifest`]($app-manifest), and `resolved` from [`$app/paths`]($app-paths) instead. + +## `@sveltejs/kit` + +### `error`, `isHttpError`, `redirect`, and `isRedirect` refer to public types + +`error`, `isHttpError`, `redirect`, and `isRedirect` now refer to the public types rather than the internal classes. If you were importing the internal `HttpError`/`Redirect` classes from `@sveltejs/kit/internal`, or doing `instanceof` checks against them, use `isHttpError`/`isRedirect` from `@sveltejs/kit` instead. + +### `json` and `text` are deprecated + +The `json(...)` and `text(...)` helpers for generating responses are deprecated. Use `Response.json(...)` and `new Response(text)` instead. + +### `defineParams` moved to `@sveltejs/kit/params` + +The `defineParams` function for creating [param matchers](advanced-routing#Matching), along with the associated types, now live in [`@sveltejs/kit/params`](@sveltejs-kit-params). + +### Env-related types moved to `@sveltejs/kit/env` + +Types like `EnvVarConfig`, used with [`defineEnvVars`](@sveltejs-kit-env#defineEnvVars) hook, now live in `@sveltejs/kit/env`. + +### Hooks-related types moved to `@sveltejs/kit/hooks` + +Types like `Handle`, which defines the type of your [`handle`](hooks#handle) hook, now live in `@sveltejs/kit/hooks`. + +### Remote function types moved to `$app/server` + +Types describing remote functions, such as `RemoteQuery`, `RemoteForm` and `RemoteCommand`, now live in `$app/server` alongside the functions themselves. + +## `@sveltejs/kit/hooks` + +The `defineEnvVars` function has moved from `@sveltejs/kit/hooks` to `@sveltejs/kit/env`. + +## `@sveltejs/kit/node` + +The [`getRequest`](@sveltejs-kit-node#getRequest) and [`setResponse`](@sveltejs-kit-node#setResponse) helpers are now synchronous and no longer return Promises. Remove `await` from calls in custom Node servers or adapters. + +## `@sveltejs/kit/node/polyfills` (removed) + +The `@sveltejs/kit/node/polyfills` module (and the Node global shims in `adapter-node` and `adapter-netlify`) applied to Node versions that are no longer supported. Remove any `import '@sveltejs/kit/node/polyfills'` statements from your custom server code. + +## Security + +### `csrf.checkOrigin` replaced by `csrf.trustedOrigins` + +The deprecated `csrf.checkOrigin` option has been removed. CSRF protection is always on; instead of disabling it with `checkOrigin: false`, allow trusted cross-origin hosts with `csrf.trustedOrigins`. + +```js +/// file: vite.config.js +import { defineConfig } from 'vite'; +import { sveltekit } from '@sveltejs/kit/vite'; +// ---cut--- +export default defineConfig({ + plugins: [ + sveltekit({ + csrf: { + ---checkOrigin: false--- + +++trustedOrigins: ['https://trusted-site.com']+++ + } + }) + ] +}); +``` + +### Cross-origin form submissions require a `Content-Type` header + +Cross-origin mutative requests that omit a `Content-Type` header are now rejected as CSRF. Ensure cross-origin form submissions include a `Content-Type` header, or add the origin to `csrf.trustedOrigins`. + +### CORS for static assets in development is handled by Vite + +SvelteKit no longer sets `access-control-allow-origin: *` on every static asset request in development. CORS is now delegated to Vite's built-in middleware. If you rely on cross-origin access to static assets in dev, configure it in your Vite config: + +```js +/// file: vite.config.js +import { defineConfig } from 'vite'; +// ---cut--- +export default defineConfig({ + server: { ++++ cors: { origin: '*' }+++ + } +}); +``` + +## Cookies + +### Updated to `cookie` v2 + +SvelteKit now uses [`cookie`](https://npmx.dev/package/cookie) v2, which involves certain changes: + +- cookie names can only contain ASCII characters. Non-ASCII characters (including Latin-1 Supplement characters like `á`) are rejected +- the `CookieSerializeOptions` type has been renamed to `SerializeOptions` +- the `CookieParseOptions` type has been renamed to `ParseOptions` + +### Paths default to `'/'` + +When setting a cookie without an explicit `path` (which was previously forbidden), the path defaults to `'/'` rather than the current request path, meaning the cookie applies to the entire site. This matches what most developers expect. You can pass an explicit `path` if necessary: + +```ts +declare const cookies: import('@sveltejs/kit').Cookies; +declare const name: string; +declare const value: string; +// ---cut--- +cookies.set(name, value, +++{ path: '/some/path' }+++); +``` + +## Error handling + +### `App.Error` always includes `status` + +An `App.Error` object, such as the `error` prop of an `+error.svelte` component, has a `status` property reflecting the HTTP status of the error that caused it (e.g. 404 for a Not Found error, or 500 for a generic internal error), in addition to `message` and whatever properties you define in your [`app.d.ts`](types#app.d.ts) file. + +### `error(...)` arguments changed + +Previously, the second argument to [`error(...)`](@sveltejs-kit#error) could be either the `message` as a `string`, or an object containing `message` alongside any additional properties defined in `app.d.ts` (such as a tracking `code`). + +Now, the second argument must always be a `string`. If there are additional properties, they must be passed as a third argument. + +### `handleValidationError` is removed + +Validation errors are now passed to [`handleError`](hooks#handleError) with `kind: 'validation'`. The generic `{ status, message }` `error` object is safe to return, while the validation `issues` are provided separately for logging or custom handling. + +### `handleError` receives all errors + +In SvelteKit 2, `handleError` was not called in the case of _expected_ errors, which is to say those created with the [`error(...)`](@sveltejs-kit#error) helper. In SvelteKit 3, _all_ errors are passed to `handleError`. See the [docs](hooks#handleError) for more information. + +### `handleError` can influence the status code + +If you need to control the HTTP status code used to render a page in the case of an error, you can do so by returning a `status` property from `handleError` alongside any other required properties of `App.Error`. + +### Rendering errors are now handled + +Errors thrown during rendering are now always routed through `handleError` and then passed to the nearest [error boundary](../svelte/svelte-boundary). Error boundaries are automatically created for each of your `+error.svelte` components. + +If you have an async `handleError` hook in `hooks.client.ts`, enable `compilerOptions.experimental.async` in the `sveltekit(...)` plugin options of your Vite config so it can be awaited during rendering. + +### Form action responses use the `fail` status code + +Enhanced form action responses now use the HTTP status code passed to `fail(...)` instead of always returning `200`. If you inspect status codes on enhanced form submissions (for example in a `use:enhance` callback or in tests), they now reflect the value passed to `fail`. + +### Sourcemaps are applied + +Sourcemaps are generated by default, and applied to stack traces when errors occur. For this to work in production (as opposed to `vite preview`), adapters need to avoid any destructive changes (such as rebundling without generating additional — and correct — sourcemaps). For first-party adapters this is a work in progress. + +## Params + +[Param matchers](advanced-routing#Matching) are no longer files inside the `src/params` directory. Declare all matchers in a single `src/params.ts` (or `src/params.js`) file using the `defineParams` helper. A matcher can be a function that returns a parsed value (or `undefined`, if the param does not match), or a [Standard Schema](https://standardschema.dev). + +```js +/// file: src/params.js +import { defineParams } from '@sveltejs/kit/params'; +import * as v from 'valibot'; + +export const params = defineParams({ + // schema variant + integer: v.pipe(v.string(), v.toNumber()), + + // function variant + fruit: (param) => { + if (param === 'apple' || param === 'orange') { + return param; + } + } +}); +``` + +## Observability + +Server-side [instrumentation](observability) now happens automatically if a `src/instrumentation.server.js` file exists. + +To opt into [OpenTelemetry](https://opentelemetry.io/) tracing, add `tracing.server` configuration: + +```js +import { defineConfig } from 'vite'; +import { sveltekit } from '@sveltejs/kit/vite'; +/// file: vite.config.js +// ---cut--- +export default defineConfig({ + plugins: [ + sveltekit({ + +++tracing: { server: true }+++ + }) + ] +}); +``` + +## Adapters + +All first-party adapters now require SvelteKit 3, alongside these adapter-specific changes: + +### `adapter-cloudflare` + +- minimum `wrangler` is now `^4.67.0` +- `@cloudflare/workers-types` upgraded +- `platform.context` removed in favour of `platform.ctx` + +### `adapter-node` + +- bundling now happens with `rolldown` +- the `ORIGIN` environment variable is removed (set `paths.origin` in your Vite config instead) + +### `adapter-netlify` + +- output now conforms to the stable [Netlify Frameworks API](https://docs.netlify.com/build/frameworks/frameworks-api/) +- deploying/previewing with the Netlify CLI requires `v17.31.0` or later (`npm i -g netlify-cli@latest`) +- edge function build target is `es2022` + +### `adapter-vercel` + +- the `edge` runtime is no longer supported + +### Adapter API changes + +For adapter authors, there are some additional changes: + +- adapters can augment the Vite config with additional plugins +- `builder.config.kit` no longer exists — the configuration now lives at the top level +- `builder.createEntries` has been removed — use `builder.writeClient`, `builder.writeServer` and `builder.writePrerendered` directly +- `builder.compress` returns a list of compressed files +- `builder.mkdirp` and `builder.rimraf` are deprecated in favour of `node:fs` methods + +## Responses + +### 204 responses return no content + +Returning a `204` (or any empty `2xx`) response from a `+server.js` handler now results in a response with no body, per the HTTP spec, rather than a SvelteKit envelope. Code that consumed the body of such responses needs to handle the empty body. + +### `resolve` always returns a `Promise` + +The `resolve` function passed to [`handle`](hooks#handle) is now typed to always return a `Promise` rather than `MaybePromise`. + +## Server-only modules + +### Files + +Server-only modules are now designated by a filename with a `server` segment, rather than a `.server.` infix — in other words, `stuff.server.ts`, `stuff.server.test.ts` and `server.ts` are all treated as server-only modules, whereas `server.ts` previously was not. + +### Directories + +Previously, any module inside `src/lib/server` was treated as server-only. This treatment now applies to _any_ `server` directory in the project with the exception of `src/routes` and your `static` directory. + +## Remote functions + +Remove functions are still considered experimental — opt in via the `experimental.remoteFunctions` flag alongside `compilerOptions.experimental.async`: + +```js +/// file: vite.config.js +import { defineConfig } from 'vite'; +import { sveltekit } from '@sveltejs/kit/vite'; +// ---cut--- +export default defineConfig({ + plugins: [ + sveltekit({ + compilerOptions: { + experimental: { + +++async: true+++ + } + }, + experimental: { + +++remoteFunctions: true+++ + } + }) + ] +}); +``` + +### Remote module filenames + +As with server-only modules, a `remote` segment in a filename designates a remote module — `stuff.remote.ts`, `stuff.remote.test.ts` and `remote.ts` are all remote modules. + +If `experimental.remoteFunctions` is not enabled, the existence of these files will cause an error. + +### `event.url`, `event.params`, and `event.route` cannot be accessed inside queries + +Accessing `event.url`, `event.params`, or `event.route` inside a remote `query` function now throws an error. These properties are not meaningful in the context of a remote function (which can be called from anywhere). Pass any values you need explicitly as arguments to the function. + +### Errors are typed as `App.Error | undefined` + +The `error` property on remote function resources (queries, live queries, forms, prerender functions) is now typed as `App.Error | undefined` rather than `any`, as the error is always transformed by `handleError`. + +### Form submissions require `field.as(...)` + +A form control must use attributes from a field associated with the current `form` object: + +```svelte + +``` + +Manually specifying a name (``) will cause the submission to be rejected. + +## Miscellaneous + +### Links to the current page cause a refresh + +If the user clicks a link that points to the current location, SvelteKit will `refreshAll()` instead of doing nothing. + +### `data-sveltekit-*` uses `false` instead of `'off'` + +The `'off'` value for `data-sveltekit-*` link attributes has been removed in favour of `false`. + +```svelte +---
--- +++++++ +``` + +### External redirects must be opted into + +To [`redirect`](@sveltejs-kit#redirect) to an external URL you must now pass an `external` option — either `true` to allow any external URL (except `javascript:` URLs, which remain blocked), or an array of allowed origins (which _can_ include `javascript:` URLs). + +```js +import { redirect } from '@sveltejs/kit'; +// ---cut--- +redirect(307, 'https://example.com', +++{ external: true }+++); +``` + +### Universal `config` takes precedence over server `config` + +Route `config` exported from a universal `+page.js` or `+layout.js` now takes precedence over `config` exported from the corresponding `+page.server.js` or `+layout.server.js`, matching how other page options are resolved. If you export `config` from both, move the canonical export to the universal file or consolidate them. + +### Service worker registrations use `type: 'module'` + +Module service workers are now widely supported. As such, SvelteKit will bundle and register your service worker as a module, rather than as a script. diff --git a/documentation/docs/60-appendix/40-migrating.md b/documentation/docs/60-appendix/40-migrating.md index b799465e8097..3b40169ac60f 100644 --- a/documentation/docs/60-appendix/40-migrating.md +++ b/documentation/docs/60-appendix/40-migrating.md @@ -37,7 +37,7 @@ The bulk of your app, in `src/routes`, can be left where it is, but several proj ### Configuration -Your `webpack.config.js` or `rollup.config.js` should be replaced with a `svelte.config.js`, as documented [here](configuration). Svelte preprocessor options should be moved to `config.preprocess`. +Your `webpack.config.js` or `rollup.config.js` should be replaced with a `vite.config.js`, as documented [here](configuration). Svelte preprocessor options should be moved to the SvelteKit plugin `config.preprocess` setting. You will need to add an [adapter](adapters). `sapper build` is roughly equivalent to [adapter-node](adapter-node) while `sapper export` is roughly equivalent to [adapter-static](adapter-static), though you might prefer to use an adapter designed for the platform you're deploying to. @@ -89,7 +89,7 @@ The `goto`, `prefetch` and `prefetchRoutes` imports from `@sapper/app` should be The `stores` import from `@sapper/app` should be replaced — see the [Stores](migrating#Pages-and-layouts-Stores) section below. -Any files you previously imported from directories in `src/node_modules` will need to be replaced with [`$lib`]($lib) imports. +Any files you previously imported from directories in `src/node_modules` will need to be replaced with [`#lib`]($lib) imports. ### Preload @@ -134,7 +134,7 @@ This caused problems and is no longer the case in SvelteKit. Instead, relative U ### <a> attributes - `sapper:prefetch` is now `data-sveltekit-preload-data` -- `sapper:noscroll` is now `data-sveltekit-noscroll` +- `sapper:noscroll` is now `data-sveltekit-reset="false"` ## Endpoints @@ -160,7 +160,7 @@ declare module 'html-minifier'; // @filename: index.js // ---cut--- import { minify } from 'html-minifier'; -import { building } from '$app/environment'; +import { building } from '$app/env'; const minification_options = { collapseBooleanAttributes: true, @@ -181,7 +181,7 @@ const minification_options = { sortClassName: true }; -/** @type {import('@sveltejs/kit').Handle} */ +/** @type {import('@sveltejs/kit/hooks').Handle} */ export async function handle({ event, resolve }) { let page = ''; diff --git a/documentation/docs/60-appendix/60-glossary.md b/documentation/docs/60-appendix/60-glossary.md index 7186b86f62e1..8fedfe40602f 100644 --- a/documentation/docs/60-appendix/60-glossary.md +++ b/documentation/docs/60-appendix/60-glossary.md @@ -40,7 +40,7 @@ For content to be prerenderable, any two users hitting it directly must get the Pre-rendered pages are not limited to static content. You can build personalized pages if user-specific data is fetched and rendered client-side. This is subject to the caveat that you will experience the downsides of not doing SSR for that content as discussed above. -In SvelteKit, you can control prerendering with [the `prerender` page option](page-options#prerender) and [`prerender` config](configuration#prerender) in `svelte.config.js`. +In SvelteKit, you can control prerendering with [the `prerender` page option](page-options#prerender) and [`prerender` config](configuration#prerender) of the SvelteKit plugin in `vite.config.js`. ## PWA @@ -63,7 +63,7 @@ In SvelteKit, you can [build SPAs with `adapter-static`](single-page-apps). Static Site Generation (SSG) is a term that refers to a site where every page is prerendered. One benefit of fully prerendering a site is that you do not need to maintain or pay for servers to perform SSR. Once generated, the site can be served from CDNs, leading to great “time to first byte” performance. This delivery model is often referred to as JAMstack. -In SvelteKit, you can do static site generation by using [`adapter-static`](adapter-static) or by configuring every page to be [prerendered](#Prerendering) using [the `prerender` page option](page-options#prerender) or [`prerender` config](configuration#prerender) in `svelte.config.js`. +In SvelteKit, you can do static site generation by using [`adapter-static`](adapter-static) or by configuring every page to be [prerendered](#Prerendering) using [the `prerender` page option](page-options#prerender) or [`prerender` config](configuration#prerender) of the SvelteKit plugin in `vite.config.js`. ## SSR diff --git a/documentation/docs/98-reference/10-@sveltejs-kit.md b/documentation/docs/98-reference/10-@sveltejs-kit.md index a89badb6359f..78a25fbc07c6 100644 --- a/documentation/docs/98-reference/10-@sveltejs-kit.md +++ b/documentation/docs/98-reference/10-@sveltejs-kit.md @@ -8,4 +8,4 @@ title: @sveltejs/kit The following are referenced by the public types documented above, but cannot be imported directly: -> TYPES: Private types +> TYPES: Private types {depth=3} diff --git a/documentation/docs/98-reference/15-@sveltejs-kit-node-polyfills.md b/documentation/docs/98-reference/15-@sveltejs-kit-node-polyfills.md deleted file mode 100644 index a80c80ab3139..000000000000 --- a/documentation/docs/98-reference/15-@sveltejs-kit-node-polyfills.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: @sveltejs/kit/node/polyfills ---- - -> MODULE: @sveltejs/kit/node/polyfills diff --git a/documentation/docs/98-reference/15-@sveltejs-kit-params.md b/documentation/docs/98-reference/15-@sveltejs-kit-params.md new file mode 100644 index 000000000000..401fc98c68df --- /dev/null +++ b/documentation/docs/98-reference/15-@sveltejs-kit-params.md @@ -0,0 +1,5 @@ +--- +title: @sveltejs/kit/params +--- + +> MODULE: @sveltejs/kit/params diff --git a/documentation/docs/98-reference/19-$app-env.md b/documentation/docs/98-reference/19-$app-env.md index 88531a242957..a0ff228db374 100644 --- a/documentation/docs/98-reference/19-$app-env.md +++ b/documentation/docs/98-reference/19-$app-env.md @@ -2,6 +2,4 @@ title: $app/env --- -> [!NOTE] This is an alias of [`$app/environment`]($app-environment), used when [explicit environment variables](environment-variables#Explicit-environment-variables) are enabled. - > MODULE: $app/env diff --git a/documentation/docs/98-reference/20-$app-env-private.md b/documentation/docs/98-reference/20-$app-env-private.md index 51c2ea22124d..7944fea1f49d 100644 --- a/documentation/docs/98-reference/20-$app-env-private.md +++ b/documentation/docs/98-reference/20-$app-env-private.md @@ -2,8 +2,6 @@ title: $app/env/private --- -Private [environment variables](environment-variables) defined in `src/env.ts` (or `src/env.js`). - -To use this module, you must enable the `experimental.explicitEnvironmentVariables` flag in your project configuration. - +Private environment variables defined in `src/env.ts` (or `src/env.js`). +See the [Environment variables](environment-variables) page for more information. diff --git a/documentation/docs/98-reference/20-$app-env-public.md b/documentation/docs/98-reference/20-$app-env-public.md index 6845c96858a9..862e1664a4a0 100644 --- a/documentation/docs/98-reference/20-$app-env-public.md +++ b/documentation/docs/98-reference/20-$app-env-public.md @@ -2,8 +2,6 @@ title: $app/env/public --- -Public [environment variables](environment-variables) defined in `src/env.ts` (or `src/env.js`). - -To use this module, you must enable the `experimental.explicitEnvironmentVariables` flag in your project configuration. - +Public environment variables defined in `src/env.ts` (or `src/env.js`). +See the [Environment variables](environment-variables) page for more information. diff --git a/documentation/docs/98-reference/20-$app-environment.md b/documentation/docs/98-reference/20-$app-environment.md deleted file mode 100644 index 4f81897214c5..000000000000 --- a/documentation/docs/98-reference/20-$app-environment.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: $app/environment ---- - -> MODULE: $app/environment diff --git a/documentation/docs/98-reference/20-$app-manifest.md b/documentation/docs/98-reference/20-$app-manifest.md new file mode 100644 index 000000000000..9421c0e9f356 --- /dev/null +++ b/documentation/docs/98-reference/20-$app-manifest.md @@ -0,0 +1,5 @@ +--- +title: $app/manifest +--- + +> MODULE: $app/manifest diff --git a/documentation/docs/98-reference/20-$app-paths.md b/documentation/docs/98-reference/20-$app-paths.md index 32a350965e4a..b5a1cb781b68 100644 --- a/documentation/docs/98-reference/20-$app-paths.md +++ b/documentation/docs/98-reference/20-$app-paths.md @@ -3,3 +3,6 @@ title: $app/paths --- > MODULE: $app/paths + +> [!LEGACY] +> `base`, `assets`, and `resolveRoute` were removed in 3.0 diff --git a/documentation/docs/98-reference/20-$app-service-worker.md b/documentation/docs/98-reference/20-$app-service-worker.md new file mode 100644 index 000000000000..796d4adbe735 --- /dev/null +++ b/documentation/docs/98-reference/20-$app-service-worker.md @@ -0,0 +1,7 @@ +--- +title: $app/service-worker +--- + +This module can only be imported in service workers. + +> MODULE: $app/service-worker diff --git a/documentation/docs/98-reference/20-$app-state.md b/documentation/docs/98-reference/20-$app-state.md index 9362eacad82f..cbb39bc4b609 100644 --- a/documentation/docs/98-reference/20-$app-state.md +++ b/documentation/docs/98-reference/20-$app-state.md @@ -4,7 +4,4 @@ title: $app/state SvelteKit makes three read-only state objects available via the `$app/state` module — `page`, `navigating` and `updated`. -> [!NOTE] -> This module was added in 2.12. If you're using an earlier version of SvelteKit, use [`$app/stores`]($app-stores) instead. - > MODULE: $app/state diff --git a/documentation/docs/98-reference/20-$app-stores.md b/documentation/docs/98-reference/20-$app-stores.md deleted file mode 100644 index ff7cf8999e82..000000000000 --- a/documentation/docs/98-reference/20-$app-stores.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: $app/stores ---- - -This module contains store-based equivalents of the exports from [`$app/state`]($app-state). If you're using SvelteKit 2.12 or later, use that module instead. - -> MODULE: $app/stores diff --git a/documentation/docs/98-reference/20-$app-tsconfig.md b/documentation/docs/98-reference/20-$app-tsconfig.md new file mode 100644 index 000000000000..9da67fb2fad1 --- /dev/null +++ b/documentation/docs/98-reference/20-$app-tsconfig.md @@ -0,0 +1,23 @@ +--- +title: $app/tsconfig +--- + +This module contains TypeScript configuration tailored for your app. Your own config should extend it — a typical `tsconfig.json` looks like this: + +```json +/// file: tsconfig.json +{ + "extends": "$app/tsconfig", + "include": ["src", "test"], + "exclude": ["src/service-worker"] +} +``` + +You can extend this configuration with your own `compilerOptions`. Overriding the following properties may cause things to break — SvelteKit will warn you if this happens: + +- `paths` — this is derived from the (deprecated) [`alias`](configuration#alias) config option, together with any [subpath imports](https://nodejs.org/api/packages.html#subpath-imports) specified in your `package.json`, to align behaviour between Vite and TypeScript. Ideally, configure subpath imports rather than using `paths` directly +- `types` — your app needs to be able to 'see' generated module declarations for things like [environment variables](environment-variables), and as such this array must include `"$app/types"` +- `isolatedModules` — must be `true`, as Vite compiles modules one at a time +- `verbatimModuleSyntax` — must be `true`, so that you can safely use type imports in `.svelte` files + +Note that the example configuration above excludes `src/service-worker`, because service workers need to be in their own TypeScript project. If you are using a service worker, create a `src/service-worker/tsconfig.json` that extends [`$app/tsconfig/service-worker`]($app-tsconfig-service-worker). diff --git a/documentation/docs/98-reference/20-$app-types.md b/documentation/docs/98-reference/20-$app-types.md deleted file mode 100644 index 97053e762869..000000000000 --- a/documentation/docs/98-reference/20-$app-types.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: $app/types ---- - -This module contains generated types for the routes in your app. - -
-

Available since 2.26

-
- -```js -// @noErrors -import type { RouteId, RouteParams, LayoutParams } from '$app/types'; -``` - -## Asset - -A union of all the filenames of assets contained in your `static` directory, plus a `string` wildcard for asset paths generated from `import` declarations. - -
- -```dts -type Asset = '/favicon.png' | '/robots.txt' | (string & {}); -``` - -
- -## RouteId - -A union of all the route IDs in your app. Used for `page.route.id` and `event.route.id`. - -
- -```dts -type RouteId = '/' | '/my-route' | '/my-other-route/[param]'; -``` - -
- -## Pathname - -A union of all valid pathnames in your app. - -
- -```dts -type Pathname = '/' | '/my-route' | `/my-other-route/${string}` & {}; -``` - -
- -## ResolvedPathname - -Similar to `Pathname`, but possibly prefixed with a [base path](configuration#paths). Used for `page.url.pathname`. - -
- -```dts -type ResolvedPathname = `${'' | `/${string}`}/` | `${'' | `/${string}`}/my-route` | `${'' | `/${string}`}/my-other-route/${string}` | {}; -``` - -
- -## RouteParams - -A utility for getting the parameters associated with a given route. - -```ts -// @errors: 2552 -type BlogParams = RouteParams<'/blog/[slug]'>; // { slug: string } -``` - -
- -```dts -type RouteParams = { /* generated */ } | Record; -``` - -
- -## LayoutParams - -A utility for getting the parameters associated with a given layout, which is similar to `RouteParams` but also includes optional parameters for any child route. - -
- -```dts -type RouteParams = { /* generated */ } | Record; -``` - -
diff --git a/documentation/docs/98-reference/21-$app-tsconfig-service-worker.md b/documentation/docs/98-reference/21-$app-tsconfig-service-worker.md new file mode 100644 index 000000000000..1c8fa148dab4 --- /dev/null +++ b/documentation/docs/98-reference/21-$app-tsconfig-service-worker.md @@ -0,0 +1,14 @@ +--- +title: $app/tsconfig/service-worker +--- + +This module contains TypeScript configuration tailored for your service worker: + +```json +/// file: src/service-worker/tsconfig.json +{ + "extends": "$app/tsconfig/service-worker" +} +``` + +You can extend this configuration with your own `compilerOptions`, adhering to the same restrictions as [`$app/tsconfig`]($app-tsconfig). diff --git a/documentation/docs/98-reference/22-$app-types.md b/documentation/docs/98-reference/22-$app-types.md new file mode 100644 index 000000000000..0f156717a2fd --- /dev/null +++ b/documentation/docs/98-reference/22-$app-types.md @@ -0,0 +1,119 @@ +--- +title: $app/types +--- + +This module contains generated types for the routes in your app. + +
+

Available since 2.26

+
+ +```js +// @noErrors +import type { RouteId, PageRouteId, EndpointRouteId, RouteParams, LayoutParams } from '$app/types'; +``` + +## AssetPath + +A union of all the filenames of assets contained in your `static` directory, relative to the `base` path. + +
+ +```dts +type AssetPath = 'favicon.png' | 'robots.txt' | (string & {}); +``` + +
+ +## RouteId + +A union of all the route IDs in your app — the union of `PageRouteId` and `EndpointRouteId`. Used for `page.route.id` and `event.route.id`. + +
+ +```dts +type RouteId = '/' | '/my-route' | '/my-other-route/[param]' | '/my-endpoint'; +``` + +
+ +## PageRouteId + +A union of the route IDs in your app that have a `+page`. + +A route ID can be in both `PageRouteId` and `EndpointRouteId`, if its directory contains both a `+page` and a `+server`. In the example below, `/my-route` has both. + +
+ +```dts +type PageRouteId = '/' | '/my-route' | '/my-other-route/[param]'; +``` + +
+ +## EndpointRouteId + +A union of the route IDs in your app that have a `+server`. + +A route ID can be in both `PageRouteId` and `EndpointRouteId`, if its directory contains both a `+page` and a `+server`. In the example below, `/my-route` has both. + +
+ +```dts +type EndpointRouteId = '/my-route' | '/my-endpoint'; +``` + +
+ +## Path + +A union of all valid paths in your app, relative to the `base` path. + +
+ +```dts +type Path = '' | 'my-route' | `my-other-route/${string}` & {}; +``` + +
+ +## ResolvedPathname + +Similar to `Path`, but prefixed with a [base path](configuration#paths). Used for `page.url.pathname`. + +
+ +```dts +type ResolvedPathname = `${'' | `/${string}`}/` | `${'' | `/${string}`}/my-route` | `${'' | `/${string}`}/my-other-route/${string}` | {}; +``` + +
+ +## RouteParams + +A utility for getting the parameters associated with a given route. + +```ts +// @errors: 2552 +type BlogParams = RouteParams<'/blog/[slug]'>; // { slug: string } +``` + +
+ +```dts +type RouteParams = { /* generated */ } | Record; +``` + +
+ +## LayoutParams + +A utility for getting the parameters associated with a given layout, which is similar to `RouteParams` but also includes optional parameters for any child route. It accepts the route ID of any directory containing a layout, including layout-only directories that are not part of `RouteId`. + +
+ +```dts +type LayoutParams = { /* generated */ }; +``` + +
diff --git a/documentation/docs/98-reference/25-$env-dynamic-private.md b/documentation/docs/98-reference/25-$env-dynamic-private.md deleted file mode 100644 index e16a1b68dec8..000000000000 --- a/documentation/docs/98-reference/25-$env-dynamic-private.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: $env/dynamic/private ---- - -> MODULE: $env/dynamic/private diff --git a/documentation/docs/98-reference/25-$env-dynamic-public.md b/documentation/docs/98-reference/25-$env-dynamic-public.md deleted file mode 100644 index 6f6ee967fafa..000000000000 --- a/documentation/docs/98-reference/25-$env-dynamic-public.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: $env/dynamic/public ---- - -> MODULE: $env/dynamic/public diff --git a/documentation/docs/98-reference/25-$env-static-private.md b/documentation/docs/98-reference/25-$env-static-private.md deleted file mode 100644 index d33ddd685c59..000000000000 --- a/documentation/docs/98-reference/25-$env-static-private.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: $env/static/private ---- - -> MODULE: $env/static/private diff --git a/documentation/docs/98-reference/25-$env-static-public.md b/documentation/docs/98-reference/25-$env-static-public.md deleted file mode 100644 index 590aabd8efc5..000000000000 --- a/documentation/docs/98-reference/25-$env-static-public.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: $env/static/public ---- - -> MODULE: $env/static/public diff --git a/documentation/docs/98-reference/26-$lib.md b/documentation/docs/98-reference/26-$lib.md index 8781f5ce863a..b0de43071244 100644 --- a/documentation/docs/98-reference/26-$lib.md +++ b/documentation/docs/98-reference/26-$lib.md @@ -1,8 +1,22 @@ --- -title: $lib +title: '#lib' --- -SvelteKit automatically makes files under `src/lib` available using the `$lib` import alias. +When scaffolding a new SvelteKit project through the [`sv` CLI](/docs/cli/overview), it automatically creates a `#lib` import alias for your `src/lib` directory, by adding the following to your `package.json`: + +```json +{ + "imports": { + "#lib": "./src/lib/index.js", + "#lib/*": "./src/lib/*" + } +} +``` + +The `#` prefix leverages Node's built-in [subpath imports](https://nodejs.org/api/packages.html#subpath-imports) feature, which reserves `#` for package-internal aliases. Vite and TypeScript both resolve these natively. + +> [!LEGACY] +> Previously, this alias was `$lib` and was automatically configured by SvelteKit. It is now `#lib` and must be declared in your `package.json` `imports` field. `import { foo } from '$lib/foo.js'` becomes `import { foo } from '#lib/foo.js'`. ```svelte @@ -12,7 +26,7 @@ A reusable component ```svelte diff --git a/documentation/docs/98-reference/27-$service-worker.md b/documentation/docs/98-reference/27-$service-worker.md deleted file mode 100644 index a8f7388ba7a9..000000000000 --- a/documentation/docs/98-reference/27-$service-worker.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: $service-worker ---- - -> MODULE: $service-worker diff --git a/documentation/docs/98-reference/50-configuration.md b/documentation/docs/98-reference/50-configuration.md deleted file mode 100644 index c4208cd17e8f..000000000000 --- a/documentation/docs/98-reference/50-configuration.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: Configuration ---- - -Your project's configuration lives in a `svelte.config.js` file at the root of your project. As well as SvelteKit, this config object is used by other tooling that integrates with Svelte such as editor extensions. - -```js -/// file: svelte.config.js -// @filename: ambient.d.ts -declare module '@sveltejs/adapter-auto' { - const plugin: () => import('@sveltejs/kit').Adapter; - export default plugin; -} - -// @filename: index.js -// ---cut--- -import adapter from '@sveltejs/adapter-auto'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter() - } -}; - -export default config; -``` - -Since version 2.62.0 you can also pass your configuration to the `sveltekit` plugin in your Vite config, along with the Svelte compiler options: - -```js -/// file: vite.config.js -// @filename: ambient.d.ts -declare module '@sveltejs/adapter-auto' { - const plugin: () => import('@sveltejs/kit').Adapter; - export default plugin; -} - -// @filename: index.js -// ---cut--- -import adapter from '@sveltejs/adapter-auto'; -import { sveltekit } from '@sveltejs/kit/vite'; -import { defineConfig } from 'vite'; - -export default defineConfig({ - plugins: [ - sveltekit({ - compilerOptions: { - experimental: { - async: true - } - }, - adapter: adapter(), - experimental: { - remoteFunctions: true - } - }) - ] -}); -``` - -> [!NOTE] The `kit` namespace is at the same level as the other top level entries; this is the only difference to the `svelte.config.js` layout. - -Any options that don't belong to SvelteKit are passed through to [`vite-plugin-svelte`](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/config.md), so you can set options like `inspector` here too. The `experimental` namespace is shared — SvelteKit reads its own flags and forwards the rest. - -If the config is defined via the plugin, the `svelte.config.js` file is ignored. - -## Config - -> TYPES: Configuration#Config - -## KitConfig - -The `kit` property configures SvelteKit, and can have the following properties: - -> EXPANDED_TYPES: Configuration#KitConfig diff --git a/documentation/docs/98-reference/54-types.md b/documentation/docs/98-reference/54-types.md index d0393ad89441..303609691982 100644 --- a/documentation/docs/98-reference/54-types.md +++ b/documentation/docs/98-reference/54-types.md @@ -119,88 +119,9 @@ Starting with version 2.16.0, two additional helper types are provided: `PagePro > > ``` -> [!NOTE] For this to work, your own `tsconfig.json` or `jsconfig.json` should extend from the generated `.svelte-kit/tsconfig.json` (where `.svelte-kit` is your [`outDir`](configuration#outDir)): +> [!NOTE] For this to work, your own `tsconfig.json` or `jsconfig.json` should extend from the generated `$app/types`: > -> `{ "extends": "./.svelte-kit/tsconfig.json" }` - -### Default tsconfig.json - -The generated `.svelte-kit/tsconfig.json` file contains a mixture of options. Some are generated programmatically based on your project configuration, and should generally not be overridden without good reason: - -```json -/// file: .svelte-kit/tsconfig.json -{ - "compilerOptions": { - "paths": { - "$lib": ["../src/lib"], - "$lib/*": ["../src/lib/*"] - }, - "rootDirs": ["..", "./types"] - }, - "include": [ - "ambient.d.ts", - "non-ambient.d.ts", - "./types/**/$types.d.ts", - "../vite.config.js", - "../vite.config.ts", - "../src/**/*.js", - "../src/**/*.ts", - "../src/**/*.svelte", - "../tests/**/*.js", - "../tests/**/*.ts", - "../tests/**/*.svelte" - ], - "exclude": [ - "../node_modules/**", - "../src/service-worker.js", - "../src/service-worker/**/*.js", - "../src/service-worker.ts", - "../src/service-worker/**/*.ts", - "../src/service-worker.d.ts", - "../src/service-worker/**/*.d.ts" - ] -} -``` - -Others are required for SvelteKit to work properly, and should also be left untouched unless you know what you're doing: - -```json -/// file: .svelte-kit/tsconfig.json -{ - "compilerOptions": { - // this ensures that types are explicitly - // imported with `import type`, which is - // necessary as Svelte/Vite cannot - // otherwise compile components correctly - "verbatimModuleSyntax": true, - - // Vite compiles one TypeScript module - // at a time, rather than compiling - // the entire module graph - "isolatedModules": true, - - // Tell TS it's used only for type-checking - "noEmit": true, - - // This ensures both `vite build` - // and `svelte-package` work correctly - "lib": ["esnext", "DOM", "DOM.Iterable"], - "moduleResolution": "bundler", - "module": "esnext", - "target": "esnext" - } -} -``` - -Use the [`typescript.config` setting](configuration#typescript) in `svelte.config.js` to extend or modify the generated `tsconfig.json`. - -## $lib - -This is a simple alias to `src/lib`. It allows you to access common components and utility modules without `../../../../` nonsense. - -### $lib/server - -A subdirectory of `$lib`. SvelteKit will prevent you from importing any modules in `$lib/server` into client-side code. See [server-only modules](server-only-modules). +> `{ "extends": "$app/tsconfig" }` ## app.d.ts diff --git a/documentation/docs/99-legacy-reference/10-$service-worker.md b/documentation/docs/99-legacy-reference/10-$service-worker.md new file mode 100644 index 000000000000..ab638b18c5a8 --- /dev/null +++ b/documentation/docs/99-legacy-reference/10-$service-worker.md @@ -0,0 +1,26 @@ +--- +title: $service-worker +--- + +The `service-worker` module existed in SvelteKit 2, and provided access to the following exports: + +## base + +A root-relative path representing the application's base path. Use [`resolve(...)`]($app-paths#resolve) from `$app/paths` instead. + +## build + +A `string[]` array of files generated by Vite. Empty during development. Use [`immutable`]($app-manifest#immutable) from `$app/manifest` instead. + +## files + +A `string[]` array of files in your `static` directory. Use [`assets`]($app-manifest#assets) from `$app/manifest` instead. + + +## prerendered + +A `string[]` array of prerendered pages. Empty during dev. Use [`prerendered`]($app-manifest#prerendered) from `$app/manifest` instead. + +## version + +The value of [`config.version.name`](configuration#version), used for populating caches. Use [`version`]($app-env#version) from `$app/env` instead. diff --git a/documentation/docs/99-legacy-reference/20-$app-environment.md b/documentation/docs/99-legacy-reference/20-$app-environment.md new file mode 100644 index 000000000000..2968e7f45367 --- /dev/null +++ b/documentation/docs/99-legacy-reference/20-$app-environment.md @@ -0,0 +1,5 @@ +--- +title: $app/environment +--- + +This module was deprecated in SvelteKit 3 in favour of [$app/env]($app-env). It will be removed in SvelteKit 4. diff --git a/documentation/docs/99-legacy-reference/20-$app-stores.md b/documentation/docs/99-legacy-reference/20-$app-stores.md new file mode 100644 index 000000000000..e33c307095d2 --- /dev/null +++ b/documentation/docs/99-legacy-reference/20-$app-stores.md @@ -0,0 +1,5 @@ +--- +title: $app/stores +--- + +This module contained store-based equivalents of the exports from [`$app/state`]($app-state) but was removed in 3.0. diff --git a/documentation/docs/99-legacy-reference/25-$env-dynamic-private.md b/documentation/docs/99-legacy-reference/25-$env-dynamic-private.md new file mode 100644 index 000000000000..72b7391d4881 --- /dev/null +++ b/documentation/docs/99-legacy-reference/25-$env-dynamic-private.md @@ -0,0 +1,5 @@ +--- +title: $env/dynamic/private +--- + +This module was deprecated in SvelteKit 3 in favour of [explicit environment variables](environment-variables). It will be removed in SvelteKit 4. diff --git a/documentation/docs/99-legacy-reference/25-$env-dynamic-public.md b/documentation/docs/99-legacy-reference/25-$env-dynamic-public.md new file mode 100644 index 000000000000..793995e9ba96 --- /dev/null +++ b/documentation/docs/99-legacy-reference/25-$env-dynamic-public.md @@ -0,0 +1,5 @@ +--- +title: $env/dynamic/public +--- + +This module was deprecated in SvelteKit 3 in favour of [explicit environment variables](environment-variables). It will be removed in SvelteKit 4. diff --git a/documentation/docs/99-legacy-reference/25-$env-static-private.md b/documentation/docs/99-legacy-reference/25-$env-static-private.md new file mode 100644 index 000000000000..19b14e4e2ce1 --- /dev/null +++ b/documentation/docs/99-legacy-reference/25-$env-static-private.md @@ -0,0 +1,5 @@ +--- +title: $env/static/private +--- + +This module was deprecated in SvelteKit 3 in favour of [explicit environment variables](environment-variables). It will be removed in SvelteKit 4. diff --git a/documentation/docs/99-legacy-reference/25-$env-static-public.md b/documentation/docs/99-legacy-reference/25-$env-static-public.md new file mode 100644 index 000000000000..7e936817d3d5 --- /dev/null +++ b/documentation/docs/99-legacy-reference/25-$env-static-public.md @@ -0,0 +1,5 @@ +--- +title: $env/static/public +--- + +This module was deprecated in SvelteKit 3 in favour of [explicit environment variables](environment-variables). It will be removed in SvelteKit 4. diff --git a/documentation/docs/99-legacy-reference/index.md b/documentation/docs/99-legacy-reference/index.md new file mode 100644 index 000000000000..e30833e4fd86 --- /dev/null +++ b/documentation/docs/99-legacy-reference/index.md @@ -0,0 +1,3 @@ +--- +title: Legacy +--- diff --git a/eslint.config.js b/eslint.config.js index 4c4aaefb917c..278bde55114d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,5 +1,7 @@ import svelte_config from '@sveltejs/eslint-config'; +import noExportsToRuntimeImports from './.eslint/no-exports-to-runtime-imports.js'; import noRuntimeToExportsImports from './.eslint/no-runtime-to-exports-imports.js'; +import requirePathToFileURL from './.eslint/require-path-to-file-url.js'; /** @type {import('eslint').Linter.Config[]} */ export default [ @@ -11,6 +13,19 @@ export default [ 'svelte/prefer-svelte-reactivity': 'off' } }, + { + files: ['packages/kit/src/exports/**/*.js'], + plugins: { + 'kit-exports-custom': { + rules: { + 'no-exports-to-runtime-imports': noExportsToRuntimeImports + } + } + }, + rules: { + 'kit-exports-custom/no-exports-to-runtime-imports': 'error' + } + }, { files: ['packages/kit/src/runtime/**/*.js'], plugins: { @@ -24,6 +39,28 @@ export default [ 'kit-custom/no-runtime-to-exports-imports': 'error' } }, + { + // code that runs in Node, where dynamic imports of absolute paths + // need `pathToFileURL` to work on Windows + files: [ + 'packages/kit/src/**/*.js', + 'packages/adapter-*/*.js', + 'packages/adapter-*/src/**/*.js', + 'packages/package/src/**/*.js' + ], + // the client runtime's dynamic imports are resolved by vite, not Node + ignores: ['packages/kit/src/runtime/**'], + plugins: { + 'kit-node-custom': { + rules: { + 'require-path-to-file-url': requirePathToFileURL + } + } + }, + rules: { + 'kit-node-custom/require-path-to-file-url': 'error' + } + }, { ignores: [ '**/.svelte-kit', @@ -33,19 +70,29 @@ export default [ '**/test-results', '**/dist', '**/.custom-out-dir', - 'packages/adapter-*/files', + 'packages/adapter-node/files', 'packages/kit/src/core/config/fixtures/multiple', // dir contains svelte config with multiple extensions tripping eslint + 'packages/kit/src/core/sync/create_manifest_data/test/samples/**/*', + 'packages/kit/src/core/sync/write_types/test/*/**/*', 'packages/kit/types/index.d.ts', // generated file + 'packages/*/test/apps/**/*', 'packages/*/test/**/build/**', - 'packages/package/test/fixtures/typescript-svelte-config/expected', + 'packages/kit/test/build-errors/**/*', + 'packages/kit/test/prerendering/**/*', + 'packages/test-redirect-importer/index.js', 'packages/package/test/errors/**/*', - 'packages/package/test/fixtures/**/*' + 'packages/package/test/fixtures/**/*', + 'packages/package/test/watch/expected/**/*', + 'packages/package/test/watch/package/**/*', + 'packages/adapter-node/smoke.spec_disabled.js' ] }, { languageOptions: { parserOptions: { - projectService: true + projectService: { + allowDefaultProject: ['packages/kit/src/runtime/app/service-worker/index.js'] + } } }, rules: { @@ -53,6 +100,17 @@ export default [ '@typescript-eslint/no-unused-expressions': 'off', '@typescript-eslint/require-await': 'error', '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-misused-promises': [ + 'error', + { + // we turn these off because it's common to pass an async callback to + // a synchronous callback parameter such as `setTimeout(...)` + checksVoidReturn: { + arguments: false, + properties: false + } + } + ], '@typescript-eslint/no-unused-vars': [ 'error', { @@ -65,20 +123,6 @@ export default [ ignoreRestSiblings: true } ] - }, - ignores: [ - 'packages/adapter-cloudflare/test/apps/**/*', - 'packages/adapter-netlify/test/apps/**/*', - 'packages/adapter-node/rollup.config.js', - 'packages/adapter-node/smoke.spec_disabled.js', - 'packages/adapter-static/test/apps/**/*', - 'packages/adapter-vercel/test/apps/**/*', - 'packages/kit/src/core/sync/create_manifest_data/test/samples/**/*', - 'packages/kit/test/apps/**/*', - 'packages/kit/test/build-errors/**/*', - 'packages/kit/test/prerendering/**/*', - 'packages/test-redirect-importer/index.js', - 'packages/adapter-netlify/test/preview.js' - ] + } } ]; diff --git a/package.json b/package.json index a42dedd6c0f3..1bd061b71311 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,9 @@ "type": "module", "scripts": { "test:kit": "pnpm run --dir packages/kit test", + "test:kit:unit": "cd packages/kit && vitest run -c vitest.kit.config.js", + "test:kit:dev": "pnpm run --dir packages/kit test:dev", + "test:kit:build": "pnpm run --dir packages/kit test:build", "test:cross-platform:dev": "pnpm run --dir packages/kit test:cross-platform:dev", "test:cross-platform:build": "pnpm run --dir packages/kit test:cross-platform:build", "test:server-side-route-resolution:dev": "pnpm run --dir packages/kit test:server-side-route-resolution:dev", @@ -14,9 +17,11 @@ "test:svelte-async:build": "pnpm run --dir packages/kit test:svelte-async:build", "test:vite-ecosystem-ci": "pnpm --dir packages/kit test", "test:others": "pnpm -r --filter='./packages/*' --filter=!./packages/kit/ --workspace-concurrency=1 test", - "check": "pnpm -r prepublishOnly && pnpm -r check", - "lint": "pnpm -r lint && eslint --cache --cache-location node_modules/.eslintcache 'packages/**/*.js'", - "format": "pnpm -r format", + "test:others:unit": "pnpm -r --filter=\"./packages/*\" --filter=\"!./packages/kit\" --workspace-concurrency=1 test:unit", + "test:unit": "vitest run", + "check": "pnpm --filter='./packages/**' prepublishOnly && pnpm --filter='./packages/**' --filter='!./packages/kit/test/build-errors/**' check", + "lint": "oxfmt --check . && echo '\nRunning eslint...' && eslint --cache --cache-location node_modules/.eslintcache 'packages/**/*.{js,d.ts}'", + "format": "oxfmt --write .", "precommit": "pnpm format && pnpm lint", "changeset:version": "changeset version && pnpm -r generate:version && git add --all", "changeset:release": "changeset publish", @@ -29,11 +34,11 @@ "@playwright/test": "catalog:", "@sveltejs/eslint-config": "catalog:", "eslint": "catalog:", - "prettier": "catalog:", - "prettier-plugin-svelte": "catalog:" + "oxfmt": "^0.62.0", + "vitest": "catalog:" }, - "packageManager": "pnpm@10.34.3+sha512.f2c531b08829d7be7f03c90addc266615f9c5477463e4bf1d275cb263ce9ea57cf0d5599110d94649a9bb3fed9f0b5efcea74a7016b61532a24cb9ad87860c1d", + "packageManager": "pnpm@11.21.0+sha512.521705bce689924eac72f5a3587122f362689ef6571e55ba80076fd637c11132ecffada26fad4ea79c485bfddbfd3d5a2a5b05805a77e893de71ec8a6cca3bb1", "engines": { - "pnpm": ">=9.0.0" + "pnpm": ">=11.0.0" } } diff --git a/packages/adapter-auto/CHANGELOG.md b/packages/adapter-auto/CHANGELOG.md index 4e788f2b032e..04399587626a 100644 --- a/packages/adapter-auto/CHANGELOG.md +++ b/packages/adapter-auto/CHANGELOG.md @@ -1,5 +1,47 @@ # @sveltejs/adapter-auto +## 8.0.0-next.3 + +### Major Changes + +- breaking: require SvelteKit 3 + +### Patch Changes + +- fix: convert resolved adapter path to a file URL before importing, so builds work on Windows + +- fix: allow prerelease versions of SvelteKit 3 to satisfy the peer dependency range +- Updated dependencies [[`bbd1e2f`](https://github.com/sveltejs/kit/commit/bbd1e2fcc9af58edb9846393b081be61df4789d5), [`e21d27b`](https://github.com/sveltejs/kit/commit/e21d27ba490c89da98dbad24fca364d0163137c4), [`6044d05`](https://github.com/sveltejs/kit/commit/6044d055031bc17deedc069dc4016565364bd4f4), [`7fb611f`](https://github.com/sveltejs/kit/commit/7fb611fb604e4d74cd889f5fe21ccd255f876382), [`9f06b3a`](https://github.com/sveltejs/kit/commit/9f06b3ac78be30d27b07fc00ea40bda493c573aa), [`8763327`](https://github.com/sveltejs/kit/commit/87633271ce40ac0839be9468598e965bde3cad51), [`7dc2379`](https://github.com/sveltejs/kit/commit/7dc23798cde97b5616049ba8d443a07e8c558bdc), [`a70c3eb`](https://github.com/sveltejs/kit/commit/a70c3eb31fe1e28f1532c84ec569ec7099da3344), [`89f49c7`](https://github.com/sveltejs/kit/commit/89f49c70b7fb4883cf704d2fb57d2d8595790ad2), [`f36b3a4`](https://github.com/sveltejs/kit/commit/f36b3a4cd307bd8c89e03fa191becc175e7b3077), [`1144a63`](https://github.com/sveltejs/kit/commit/1144a63d318c3c3255d06b374bb7a656a469fabf), [`9f06b3a`](https://github.com/sveltejs/kit/commit/9f06b3ac78be30d27b07fc00ea40bda493c573aa)]: + - @sveltejs/kit@3.0.0-next.15 + +## 8.0.0-next.2 + +### Patch Changes + +- fix: convert resolved adapter path to a file URL before importing, so builds work on Windows ([#16618](https://github.com/sveltejs/kit/pull/16618)) +- Updated dependencies [[`c3f58bb`](https://github.com/sveltejs/kit/commit/c3f58bb0eb6bde790897f1e8b89019bc39a062e5), [`b655dc7`](https://github.com/sveltejs/kit/commit/b655dc7cf043ff01d53e494e9744e548bb3ad9fd), [`a70cc4f`](https://github.com/sveltejs/kit/commit/a70cc4f2ca24e7099c898456085ffe18d191657e), [`b325afb`](https://github.com/sveltejs/kit/commit/b325afb509b54445f678348e88ea2bf0152f4cec), [`e76b3d7`](https://github.com/sveltejs/kit/commit/e76b3d778bacb429fdb40a41b13ff04b54d97c1f), [`58f47eb`](https://github.com/sveltejs/kit/commit/58f47eb94016c5405829667bbc873e027c7b363c), [`fe9d8d9`](https://github.com/sveltejs/kit/commit/fe9d8d939b6d1a07b2957104fda6a1123f5eebd3), [`0c7bbd7`](https://github.com/sveltejs/kit/commit/0c7bbd7bd4a346c560e7cbf9b39ba8bda7c718f0), [`08d7e2a`](https://github.com/sveltejs/kit/commit/08d7e2a1b327d4e68c217f6e7d6b305732c82b1b), [`f70f679`](https://github.com/sveltejs/kit/commit/f70f679143d4330b6d3b7d0e690145b5487df97f), [`0624b26`](https://github.com/sveltejs/kit/commit/0624b2678efd41223f70972f7bff2d81d4a5c7f9), [`0624b26`](https://github.com/sveltejs/kit/commit/0624b2678efd41223f70972f7bff2d81d4a5c7f9), [`f4222ad`](https://github.com/sveltejs/kit/commit/f4222adc504a7041b50325424a3eb86af02961ed), [`089628b`](https://github.com/sveltejs/kit/commit/089628b0d9637578085ad61b5786cd1c4476e399), [`d8617b3`](https://github.com/sveltejs/kit/commit/d8617b3f2f08144f6acfc7c636b89d62b982a33d), [`c7369a1`](https://github.com/sveltejs/kit/commit/c7369a148169dfd51af150c461ff266977b6befd), [`5e5e592`](https://github.com/sveltejs/kit/commit/5e5e592f093d05c6ea30a41a0ebf543829069c5f), [`60057c1`](https://github.com/sveltejs/kit/commit/60057c1d2a1e6429e5f315d587ebc01baef6dc94), [`f70f679`](https://github.com/sveltejs/kit/commit/f70f679143d4330b6d3b7d0e690145b5487df97f), [`38f1528`](https://github.com/sveltejs/kit/commit/38f15289e249942275eab1281516702de08b7a30), [`40092f3`](https://github.com/sveltejs/kit/commit/40092f34aa063d033fc7a4d1d6fb1c10abd8cbf6), [`ff4247e`](https://github.com/sveltejs/kit/commit/ff4247e2376862207dd9df7317f39d8bb8dd9ee4), [`79a5de5`](https://github.com/sveltejs/kit/commit/79a5de581fc1bb708879678b8295706221ba0f0f), [`a70cc4f`](https://github.com/sveltejs/kit/commit/a70cc4f2ca24e7099c898456085ffe18d191657e)]: + - @sveltejs/kit@3.0.0-next.14 + +## 8.0.0-next.1 + +### Patch Changes + +- fix: allow prerelease versions of SvelteKit 3 to satisfy the peer dependency range ([#16286](https://github.com/sveltejs/kit/pull/16286)) +- Updated dependencies [[`737d119`](https://github.com/sveltejs/kit/commit/737d119730ec163832f838b3813c9cbb75dd799f), [`fa78efb`](https://github.com/sveltejs/kit/commit/fa78efb626031497eecc3a88fa7b6f6da36eca0f), [`07c207e`](https://github.com/sveltejs/kit/commit/07c207ed919570d4ed4954f751056b4f838abdea), [`a47071b`](https://github.com/sveltejs/kit/commit/a47071bb6581fe5f6df4cc47b80e9f0f594624e5), [`14d7d5a`](https://github.com/sveltejs/kit/commit/14d7d5abd9cd8462e4c79ed6a5c10bc43258e97a), [`5c38e51`](https://github.com/sveltejs/kit/commit/5c38e515db7fbb92e5ae01db84b4f0040a02f187), [`0702baa`](https://github.com/sveltejs/kit/commit/0702baa58b2a4600e83b55ef4a85e9cbe3391a4f), [`e1938c6`](https://github.com/sveltejs/kit/commit/e1938c62348b8ccc83e186b1acad76a0a10cc9ea), [`8293144`](https://github.com/sveltejs/kit/commit/8293144b6fbfff9f97cdb1d2224a5eaa04c84fc4), [`f76d7d9`](https://github.com/sveltejs/kit/commit/f76d7d91e18a0afd1f6798c095eb8c170c108cff), [`ab5c253`](https://github.com/sveltejs/kit/commit/ab5c253efb2546ead74db4c8c9b4c7639d97e0ed), [`b557b1b`](https://github.com/sveltejs/kit/commit/b557b1ba76903e1fb0fc3bb87bddc5420332137a), [`4a513e2`](https://github.com/sveltejs/kit/commit/4a513e27ab2ad44dae0c6a4a6c8deda840008530)]: + - @sveltejs/kit@3.0.0-next.8 + +## 8.0.0-next.0 +### Major Changes + + +- breaking: require SvelteKit 3 ([#15506](https://github.com/sveltejs/kit/pull/15506)) + + +### Patch Changes + +- Updated dependencies [[`fa335bd`](https://github.com/sveltejs/kit/commit/fa335bd8af3a982d68c4d34b813b68887dc7cce8), [`3031d89`](https://github.com/sveltejs/kit/commit/3031d89087d49297e89b6566108a1a1105c38475), [`cb9d416`](https://github.com/sveltejs/kit/commit/cb9d416d05266b6237f2856fb94df5b8a9b04279), [`caf3a18`](https://github.com/sveltejs/kit/commit/caf3a18de6581ab7d454d6fa8ad37ebdbdef95e6), [`4777827`](https://github.com/sveltejs/kit/commit/4777827246727045cf0209399751377564e7951d), [`a2792e2`](https://github.com/sveltejs/kit/commit/a2792e2cdc2dee91c20c7bfd921a104b539abae5), [`ba36148`](https://github.com/sveltejs/kit/commit/ba36148577ed0bc2675034a3d57001d71d328cd1), [`48e8710`](https://github.com/sveltejs/kit/commit/48e8710889b348a039322e05f3d2672327d56184), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`047d6a0`](https://github.com/sveltejs/kit/commit/047d6a0f1c3fd1bc6f90a81177a2834b023a64b3), [`87603d1`](https://github.com/sveltejs/kit/commit/87603d13d009cada171c9b5d5daad5adad4f72de), [`096962c`](https://github.com/sveltejs/kit/commit/096962c47b0b0f63acd0d539d3973797e4867487), [`d545970`](https://github.com/sveltejs/kit/commit/d54597018308068aea5268e6c45b26398cd57607), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`d06affc`](https://github.com/sveltejs/kit/commit/d06affc9246c676fc32b644efc0bbf53b42bc67d), [`8af47eb`](https://github.com/sveltejs/kit/commit/8af47eb06f9a1a883058de059ccf81aab4e85627), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`5c4d130`](https://github.com/sveltejs/kit/commit/5c4d130ea2953464d8b4b462eb5aa4641aa78da1), [`3f11f35`](https://github.com/sveltejs/kit/commit/3f11f35125c5b3625891f0d00d9c7c826e6eaea2), [`caf3a18`](https://github.com/sveltejs/kit/commit/caf3a18de6581ab7d454d6fa8ad37ebdbdef95e6), [`8823037`](https://github.com/sveltejs/kit/commit/8823037b4a341013cc40a9bb688ee23bda3dbb04), [`1d76212`](https://github.com/sveltejs/kit/commit/1d76212740f59ed58839ef569899c653870834fd), [`0dc0548`](https://github.com/sveltejs/kit/commit/0dc0548307a439b6a6f24b14405366ced213f828), [`00d81fa`](https://github.com/sveltejs/kit/commit/00d81fab618435bac95b1bacb5e9734b61ba1af6)]: + - @sveltejs/kit@3.0.0-next.0 + ## 7.0.1 ### Patch Changes diff --git a/packages/adapter-auto/adapters.js b/packages/adapter-auto/adapters.js index 77f1990e8c7f..78deb2a87aa8 100644 --- a/packages/adapter-auto/adapters.js +++ b/packages/adapter-auto/adapters.js @@ -7,19 +7,19 @@ export const adapters = [ name: 'Vercel', test: () => !!process.env.VERCEL, module: '@sveltejs/adapter-vercel', - version: '6' + version: '7' }, { name: 'Cloudflare Pages', test: () => !!process.env.CF_PAGES, module: '@sveltejs/adapter-cloudflare', - version: '7' + version: '8' }, { name: 'Netlify', test: () => !!process.env.NETLIFY, module: '@sveltejs/adapter-netlify', - version: '6' + version: '7' }, { name: 'Azure Static Web Apps', @@ -37,6 +37,6 @@ export const adapters = [ name: 'Google Cloud Run', test: () => !!process.env.GCP_BUILDPACKS, module: '@sveltejs/adapter-node', - version: '5' + version: '6' } ]; diff --git a/packages/adapter-auto/index.js b/packages/adapter-auto/index.js index 64bc04312afc..ef8aaf7ae0a4 100644 --- a/packages/adapter-auto/index.js +++ b/packages/adapter-auto/index.js @@ -1,20 +1,10 @@ import { execSync } from 'node:child_process'; +import { pathToFileURL } from 'node:url'; import { adapters } from './adapters.js'; import path from 'node:path'; import fs from 'node:fs'; import process from 'node:process'; -/** - * @template T - * @template {keyof T} K - * @typedef {Partial> & Required>} PartialExcept - */ - -/** - * We use a custom `Builder` type here to support the minimum version of SvelteKit. - * @typedef {PartialExcept} Builder2_0_0 - */ - /** @type {Record string>} */ const commands = { npm: (name, version) => `npm install -D ${name}@${version}`, @@ -133,7 +123,7 @@ async function get_adapter() { } /** @type {{ default: () => Adapter }} */ - const module = await import(resolved); + const module = await import(pathToFileURL(resolved).href); const adapter = module.default(); @@ -146,14 +136,13 @@ async function get_adapter() { }; } -/** @type {() => Adapter} */ +/** @type {typeof import('./index.js').default} */ export default () => ({ name: '@sveltejs/adapter-auto', - /** @param {Builder2_0_0} builder */ adapt: async (builder) => { const adapter = await get_adapter(); - if (adapter) return adapter.adapt(/** @type {import('@sveltejs/kit').Builder} */ (builder)); + if (adapter) return adapter.adapt(builder); builder.log.warn( 'Could not detect a supported production environment. See https://svelte.dev/docs/kit/adapters to learn how to configure your app to run on the platform of your choosing' diff --git a/packages/adapter-auto/package.json b/packages/adapter-auto/package.json index ae2e87f09b59..16bebbbaca35 100644 --- a/packages/adapter-auto/package.json +++ b/packages/adapter-auto/package.json @@ -1,6 +1,6 @@ { "name": "@sveltejs/adapter-auto", - "version": "7.0.1", + "version": "8.0.0-next.3", "description": "Automatically chooses the SvelteKit adapter for your current environment, if possible.", "keywords": [ "adapter", @@ -28,24 +28,24 @@ }, "types": "index.d.ts", "files": [ - "files", "index.js", "index.d.ts", "adapters.js" ], "scripts": { - "lint": "prettier --check .", - "format": "pnpm lint --write", + "lint": "oxfmt --check .", + "format": "oxfmt --write .", "check": "tsc", - "test": "vitest run" + "test": "vitest run", + "test:unit": "vitest run" }, "devDependencies": { "@sveltejs/kit": "workspace:^", "@types/node": "catalog:", - "typescript": "catalog:", + "typescript": "catalog:typescript-native", "vitest": "catalog:" }, "peerDependencies": { - "@sveltejs/kit": "^2.0.0" + "@sveltejs/kit": "^3.0.0-next.0" } } diff --git a/packages/adapter-auto/vitest.config.js b/packages/adapter-auto/vitest.config.js new file mode 100644 index 000000000000..a15b3a470a6d --- /dev/null +++ b/packages/adapter-auto/vitest.config.js @@ -0,0 +1,5 @@ +// we need this file to prevent Vitest from resolving a Vitest config from another directory + +import { defineConfig } from 'vitest/config'; + +export default defineConfig({}); diff --git a/packages/adapter-cloudflare/.gitignore b/packages/adapter-cloudflare/.gitignore deleted file mode 100644 index 2c8afebd8eda..000000000000 --- a/packages/adapter-cloudflare/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/files diff --git a/packages/adapter-cloudflare/CHANGELOG.md b/packages/adapter-cloudflare/CHANGELOG.md index 372634fade86..155c264d7974 100644 --- a/packages/adapter-cloudflare/CHANGELOG.md +++ b/packages/adapter-cloudflare/CHANGELOG.md @@ -1,5 +1,102 @@ # @sveltejs/adapter-cloudflare +## 8.0.0-next.6 + +### Patch Changes + +- chore: bump `@cloudflare/worker-types` to 5.20260809.1 ([#16745](https://github.com/sveltejs/kit/pull/16745)) +- Updated dependencies [[`1742811`](https://github.com/sveltejs/kit/commit/1742811562358caf6042bed33c60ce6427a3e831), [`1611c61`](https://github.com/sveltejs/kit/commit/1611c617a1ec1b32e10627c2815fb7e6673f3157), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`13e7b18`](https://github.com/sveltejs/kit/commit/13e7b186a94b5b8317771d2b1e795b432dcd6faf), [`529346d`](https://github.com/sveltejs/kit/commit/529346d96369f92cdd804be9c4313c598efe34b8), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`81d6319`](https://github.com/sveltejs/kit/commit/81d6319eef08a765ccd3922790a7aee16e284cc2), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`69a5bdf`](https://github.com/sveltejs/kit/commit/69a5bdf9f45810eeed2faa089c8d3206463d347e)]: + - @sveltejs/kit@3.0.0-next.20 + +## 8.0.0-next.5 + +### Major Changes + +- breaking: minimum Wrangler version required is `4.118.0` ([#16668](https://github.com/sveltejs/kit/pull/16668)) + +- breaking: upgrade `@cloudflare/workers-types` to 4.20260219.0 + +- breaking: upgrade minimum `wrangler` version to ^4.67.0 + +- breaking: remove `platform.context` in favour of `platform.ctx` + +- breaking: require SvelteKit 3 + +- breaking: remove use of the Cloudflare Workers Cache API in favour of Workers Caching ([#16640](https://github.com/sveltejs/kit/pull/16640)) + +### Patch Changes + +- chore: use `node:fs` instead of deprecated `builder.rimraf` and `builder.mkdirp` ([#16286](https://github.com/sveltejs/kit/pull/16286)) + +- chore: bump `@cloudflare/workers-types` to `4.20260621.1` + +- fix: avoid overriding user's existing `_headers` rules + +- chore: check the `WORKERS_CI` environment variable to determine if we're building for Cloudflare Workers + +- fix: allow prerelease versions of SvelteKit 3 to satisfy the peer dependency range + +- fix: avoid caching immutable asset 404s +- Updated dependencies [[`bbd1e2f`](https://github.com/sveltejs/kit/commit/bbd1e2fcc9af58edb9846393b081be61df4789d5), [`e21d27b`](https://github.com/sveltejs/kit/commit/e21d27ba490c89da98dbad24fca364d0163137c4), [`6044d05`](https://github.com/sveltejs/kit/commit/6044d055031bc17deedc069dc4016565364bd4f4), [`7fb611f`](https://github.com/sveltejs/kit/commit/7fb611fb604e4d74cd889f5fe21ccd255f876382), [`9f06b3a`](https://github.com/sveltejs/kit/commit/9f06b3ac78be30d27b07fc00ea40bda493c573aa), [`8763327`](https://github.com/sveltejs/kit/commit/87633271ce40ac0839be9468598e965bde3cad51), [`7dc2379`](https://github.com/sveltejs/kit/commit/7dc23798cde97b5616049ba8d443a07e8c558bdc), [`a70c3eb`](https://github.com/sveltejs/kit/commit/a70c3eb31fe1e28f1532c84ec569ec7099da3344), [`89f49c7`](https://github.com/sveltejs/kit/commit/89f49c70b7fb4883cf704d2fb57d2d8595790ad2), [`f36b3a4`](https://github.com/sveltejs/kit/commit/f36b3a4cd307bd8c89e03fa191becc175e7b3077), [`1144a63`](https://github.com/sveltejs/kit/commit/1144a63d318c3c3255d06b374bb7a656a469fabf), [`9f06b3a`](https://github.com/sveltejs/kit/commit/9f06b3ac78be30d27b07fc00ea40bda493c573aa)]: + - @sveltejs/kit@3.0.0-next.15 + +## 8.0.0-next.4 + +### Patch Changes + +- fix: avoid caching immutable asset 404s ([#16627](https://github.com/sveltejs/kit/pull/16627)) +- Updated dependencies [[`c3f58bb`](https://github.com/sveltejs/kit/commit/c3f58bb0eb6bde790897f1e8b89019bc39a062e5), [`b655dc7`](https://github.com/sveltejs/kit/commit/b655dc7cf043ff01d53e494e9744e548bb3ad9fd), [`a70cc4f`](https://github.com/sveltejs/kit/commit/a70cc4f2ca24e7099c898456085ffe18d191657e), [`b325afb`](https://github.com/sveltejs/kit/commit/b325afb509b54445f678348e88ea2bf0152f4cec), [`e76b3d7`](https://github.com/sveltejs/kit/commit/e76b3d778bacb429fdb40a41b13ff04b54d97c1f), [`58f47eb`](https://github.com/sveltejs/kit/commit/58f47eb94016c5405829667bbc873e027c7b363c), [`fe9d8d9`](https://github.com/sveltejs/kit/commit/fe9d8d939b6d1a07b2957104fda6a1123f5eebd3), [`0c7bbd7`](https://github.com/sveltejs/kit/commit/0c7bbd7bd4a346c560e7cbf9b39ba8bda7c718f0), [`08d7e2a`](https://github.com/sveltejs/kit/commit/08d7e2a1b327d4e68c217f6e7d6b305732c82b1b), [`f70f679`](https://github.com/sveltejs/kit/commit/f70f679143d4330b6d3b7d0e690145b5487df97f), [`0624b26`](https://github.com/sveltejs/kit/commit/0624b2678efd41223f70972f7bff2d81d4a5c7f9), [`0624b26`](https://github.com/sveltejs/kit/commit/0624b2678efd41223f70972f7bff2d81d4a5c7f9), [`f4222ad`](https://github.com/sveltejs/kit/commit/f4222adc504a7041b50325424a3eb86af02961ed), [`089628b`](https://github.com/sveltejs/kit/commit/089628b0d9637578085ad61b5786cd1c4476e399), [`d8617b3`](https://github.com/sveltejs/kit/commit/d8617b3f2f08144f6acfc7c636b89d62b982a33d), [`c7369a1`](https://github.com/sveltejs/kit/commit/c7369a148169dfd51af150c461ff266977b6befd), [`5e5e592`](https://github.com/sveltejs/kit/commit/5e5e592f093d05c6ea30a41a0ebf543829069c5f), [`60057c1`](https://github.com/sveltejs/kit/commit/60057c1d2a1e6429e5f315d587ebc01baef6dc94), [`f70f679`](https://github.com/sveltejs/kit/commit/f70f679143d4330b6d3b7d0e690145b5487df97f), [`38f1528`](https://github.com/sveltejs/kit/commit/38f15289e249942275eab1281516702de08b7a30), [`40092f3`](https://github.com/sveltejs/kit/commit/40092f34aa063d033fc7a4d1d6fb1c10abd8cbf6), [`ff4247e`](https://github.com/sveltejs/kit/commit/ff4247e2376862207dd9df7317f39d8bb8dd9ee4), [`79a5de5`](https://github.com/sveltejs/kit/commit/79a5de581fc1bb708879678b8295706221ba0f0f), [`a70cc4f`](https://github.com/sveltejs/kit/commit/a70cc4f2ca24e7099c898456085ffe18d191657e)]: + - @sveltejs/kit@3.0.0-next.14 + +## 8.0.0-next.3 + +### Patch Changes + +- chore: bump `@cloudflare/workers-types` to `4.20260621.1` ([#16455](https://github.com/sveltejs/kit/pull/16455)) +- Updated dependencies [[`adc4c5b`](https://github.com/sveltejs/kit/commit/adc4c5be542ecf7da93ee8d0e90e9d13b952998b), [`ecb0701`](https://github.com/sveltejs/kit/commit/ecb0701b8826e41229f84922f37c12099c66d3bc), [`4b4cc60`](https://github.com/sveltejs/kit/commit/4b4cc60990fa940236639a6cecb6c8efe4287bfa), [`7390dbe`](https://github.com/sveltejs/kit/commit/7390dbe6ef8dff0a1cd923f96daef462a3ce68f2), [`2e71340`](https://github.com/sveltejs/kit/commit/2e713407deb22965777afb01cb8215013ef8bfd3), [`c87bc3a`](https://github.com/sveltejs/kit/commit/c87bc3ac7830f8ee4e76254c9315adcc79ff6a99), [`af15c6c`](https://github.com/sveltejs/kit/commit/af15c6cab2fbfe6bae51dc1fa77a9271df996a47), [`c6fa431`](https://github.com/sveltejs/kit/commit/c6fa43157ae0dbaa9fc61b7e8d2db673e1f9f893), [`bfe4dea`](https://github.com/sveltejs/kit/commit/bfe4deae006f61347b5a5dce7747ec96692e97f9), [`df7dc72`](https://github.com/sveltejs/kit/commit/df7dc7219451bcc86a93e93188d78d664973d9ae), [`5ae11a1`](https://github.com/sveltejs/kit/commit/5ae11a119667c1b24e3b6d17f4b538032ddf1ab1), [`781205c`](https://github.com/sveltejs/kit/commit/781205c41cc5696eb862e5c13356c301a0368983), [`c87bc3a`](https://github.com/sveltejs/kit/commit/c87bc3ac7830f8ee4e76254c9315adcc79ff6a99), [`a1bfeb9`](https://github.com/sveltejs/kit/commit/a1bfeb9de906ae140b9cdc7d7eeaf0ee652a7bcc), [`c87bc3a`](https://github.com/sveltejs/kit/commit/c87bc3ac7830f8ee4e76254c9315adcc79ff6a99), [`9f0127d`](https://github.com/sveltejs/kit/commit/9f0127d4eee3f17dc1db1d2b99b1ca5fc80af784), [`4b4cc60`](https://github.com/sveltejs/kit/commit/4b4cc60990fa940236639a6cecb6c8efe4287bfa), [`8f5b9c7`](https://github.com/sveltejs/kit/commit/8f5b9c72ae930b3b93ef9e1b9ecb9e52664e1d74), [`c9b5544`](https://github.com/sveltejs/kit/commit/c9b554448633e1839e8c0f16c374b00a5ce4d1b6), [`fe1d4a4`](https://github.com/sveltejs/kit/commit/fe1d4a4d68d1d7a3925004fe804209ff54091e34), [`4b4cc60`](https://github.com/sveltejs/kit/commit/4b4cc60990fa940236639a6cecb6c8efe4287bfa), [`5f78e95`](https://github.com/sveltejs/kit/commit/5f78e953ae3966c23a01194dd200f5c57f723e40), [`25510fc`](https://github.com/sveltejs/kit/commit/25510fc79484363277d062e01f2b872f17a473f5), [`c99a6cf`](https://github.com/sveltejs/kit/commit/c99a6cfb7fec133aa85a6de59b43fc442aedc41a), [`17a45ca`](https://github.com/sveltejs/kit/commit/17a45cadb9356403b82c170f0d0027a1ba085667), [`2ca20c3`](https://github.com/sveltejs/kit/commit/2ca20c3ee7348836757dc6f3469217e46957766b), [`eb5c973`](https://github.com/sveltejs/kit/commit/eb5c9737b6d74c53f572b3e047faa5947052ced2)]: + - @sveltejs/kit@3.0.0-next.12 + +## 8.0.0-next.2 + +### Patch Changes + +- fix: allow prerelease versions of SvelteKit 3 to satisfy the peer dependency range ([#16286](https://github.com/sveltejs/kit/pull/16286)) +- Updated dependencies [[`737d119`](https://github.com/sveltejs/kit/commit/737d119730ec163832f838b3813c9cbb75dd799f), [`fa78efb`](https://github.com/sveltejs/kit/commit/fa78efb626031497eecc3a88fa7b6f6da36eca0f), [`07c207e`](https://github.com/sveltejs/kit/commit/07c207ed919570d4ed4954f751056b4f838abdea), [`a47071b`](https://github.com/sveltejs/kit/commit/a47071bb6581fe5f6df4cc47b80e9f0f594624e5), [`14d7d5a`](https://github.com/sveltejs/kit/commit/14d7d5abd9cd8462e4c79ed6a5c10bc43258e97a), [`5c38e51`](https://github.com/sveltejs/kit/commit/5c38e515db7fbb92e5ae01db84b4f0040a02f187), [`0702baa`](https://github.com/sveltejs/kit/commit/0702baa58b2a4600e83b55ef4a85e9cbe3391a4f), [`e1938c6`](https://github.com/sveltejs/kit/commit/e1938c62348b8ccc83e186b1acad76a0a10cc9ea), [`8293144`](https://github.com/sveltejs/kit/commit/8293144b6fbfff9f97cdb1d2224a5eaa04c84fc4), [`f76d7d9`](https://github.com/sveltejs/kit/commit/f76d7d91e18a0afd1f6798c095eb8c170c108cff), [`ab5c253`](https://github.com/sveltejs/kit/commit/ab5c253efb2546ead74db4c8c9b4c7639d97e0ed), [`b557b1b`](https://github.com/sveltejs/kit/commit/b557b1ba76903e1fb0fc3bb87bddc5420332137a), [`4a513e2`](https://github.com/sveltejs/kit/commit/4a513e27ab2ad44dae0c6a4a6c8deda840008530)]: + - @sveltejs/kit@3.0.0-next.8 + +## 8.0.0-next.1 +### Patch Changes + + +- fix: avoid overriding user's existing `_headers` rules ([#16183](https://github.com/sveltejs/kit/pull/16183)) + +- Updated dependencies [[`3c434fb`](https://github.com/sveltejs/kit/commit/3c434fbf12822e19bea833a2084b329590ccf5b1), [`a9284e8`](https://github.com/sveltejs/kit/commit/a9284e8e627b1b0a8d3772c53310c41056e556b1), [`3726a7a`](https://github.com/sveltejs/kit/commit/3726a7a67c949a160283df7db6033b440e229171), [`f9d2240`](https://github.com/sveltejs/kit/commit/f9d22409aa0fca0f5035aead811fe6907f63c293), [`a9284e8`](https://github.com/sveltejs/kit/commit/a9284e8e627b1b0a8d3772c53310c41056e556b1), [`7c040ba`](https://github.com/sveltejs/kit/commit/7c040ba28e98ecbf38acbbf93653fc6c33a506ba), [`223eaad`](https://github.com/sveltejs/kit/commit/223eaadca55c6e73096cd605759d6c7366f260a2), [`223eaad`](https://github.com/sveltejs/kit/commit/223eaadca55c6e73096cd605759d6c7366f260a2), [`3b907d4`](https://github.com/sveltejs/kit/commit/3b907d449a42f9d763cf8eaa6ac5a88975137011), [`fd628a5`](https://github.com/sveltejs/kit/commit/fd628a57348d25fb433f63778e0239892ebb1da2), [`223eaad`](https://github.com/sveltejs/kit/commit/223eaadca55c6e73096cd605759d6c7366f260a2), [`6c1d035`](https://github.com/sveltejs/kit/commit/6c1d03535b09f670164eb22f05405c17120149f1), [`178eac0`](https://github.com/sveltejs/kit/commit/178eac007e648f3235bc45d926cfc029ddf8de07), [`c6562a9`](https://github.com/sveltejs/kit/commit/c6562a92cb908058cab3fabad075a94ad9cc6e39), [`8eca2ab`](https://github.com/sveltejs/kit/commit/8eca2ab8b4760456a782d7a29f83f24a15fac177), [`61cf188`](https://github.com/sveltejs/kit/commit/61cf188e38725b041d0411c6074ad2649a7c42b4)]: + - @sveltejs/kit@3.0.0-next.6 + +## 8.0.0-next.0 +### Major Changes + + +- breaking: upgrade `@cloudflare/workers-types` to 4.20260219.0 ([#15347](https://github.com/sveltejs/kit/pull/15347)) + + +- breaking: upgrade minimum `wrangler` version to ^4.67.0 ([#15347](https://github.com/sveltejs/kit/pull/15347)) + + +- breaking: remove `platform.context` in favour of `platform.ctx` ([#15347](https://github.com/sveltejs/kit/pull/15347)) + + +- breaking: require SvelteKit 3 ([#15506](https://github.com/sveltejs/kit/pull/15506)) + + +### Patch Changes + + +- chore: check the `WORKERS_CI` environment variable to determine if we're building for Cloudflare Workers ([#13733](https://github.com/sveltejs/kit/pull/13733)) + +- Updated dependencies [[`fa335bd`](https://github.com/sveltejs/kit/commit/fa335bd8af3a982d68c4d34b813b68887dc7cce8), [`3031d89`](https://github.com/sveltejs/kit/commit/3031d89087d49297e89b6566108a1a1105c38475), [`cb9d416`](https://github.com/sveltejs/kit/commit/cb9d416d05266b6237f2856fb94df5b8a9b04279), [`caf3a18`](https://github.com/sveltejs/kit/commit/caf3a18de6581ab7d454d6fa8ad37ebdbdef95e6), [`4777827`](https://github.com/sveltejs/kit/commit/4777827246727045cf0209399751377564e7951d), [`a2792e2`](https://github.com/sveltejs/kit/commit/a2792e2cdc2dee91c20c7bfd921a104b539abae5), [`ba36148`](https://github.com/sveltejs/kit/commit/ba36148577ed0bc2675034a3d57001d71d328cd1), [`48e8710`](https://github.com/sveltejs/kit/commit/48e8710889b348a039322e05f3d2672327d56184), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`047d6a0`](https://github.com/sveltejs/kit/commit/047d6a0f1c3fd1bc6f90a81177a2834b023a64b3), [`87603d1`](https://github.com/sveltejs/kit/commit/87603d13d009cada171c9b5d5daad5adad4f72de), [`096962c`](https://github.com/sveltejs/kit/commit/096962c47b0b0f63acd0d539d3973797e4867487), [`d545970`](https://github.com/sveltejs/kit/commit/d54597018308068aea5268e6c45b26398cd57607), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`d06affc`](https://github.com/sveltejs/kit/commit/d06affc9246c676fc32b644efc0bbf53b42bc67d), [`8af47eb`](https://github.com/sveltejs/kit/commit/8af47eb06f9a1a883058de059ccf81aab4e85627), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`5c4d130`](https://github.com/sveltejs/kit/commit/5c4d130ea2953464d8b4b462eb5aa4641aa78da1), [`3f11f35`](https://github.com/sveltejs/kit/commit/3f11f35125c5b3625891f0d00d9c7c826e6eaea2), [`caf3a18`](https://github.com/sveltejs/kit/commit/caf3a18de6581ab7d454d6fa8ad37ebdbdef95e6), [`8823037`](https://github.com/sveltejs/kit/commit/8823037b4a341013cc40a9bb688ee23bda3dbb04), [`1d76212`](https://github.com/sveltejs/kit/commit/1d76212740f59ed58839ef569899c653870834fd), [`0dc0548`](https://github.com/sveltejs/kit/commit/0dc0548307a439b6a6f24b14405366ced213f828), [`00d81fa`](https://github.com/sveltejs/kit/commit/00d81fab618435bac95b1bacb5e9734b61ba1af6)]: + - @sveltejs/kit@3.0.0-next.0 + ## 7.2.9 ### Patch Changes diff --git a/packages/adapter-cloudflare/ambient.d.ts b/packages/adapter-cloudflare/ambient.d.ts index 033b35b56cf0..d3c19971446c 100644 --- a/packages/adapter-cloudflare/ambient.d.ts +++ b/packages/adapter-cloudflare/ambient.d.ts @@ -10,8 +10,6 @@ declare global { // we should not type `env` here because it will override any type set by // the user in `src/app.d.ts` ctx: ExecutionContext; - /** @deprecated Use `ctx` instead */ - context: ExecutionContext; caches: CacheStorage; cf?: IncomingRequestCfProperties; } diff --git a/packages/adapter-cloudflare/files/internal.d.ts b/packages/adapter-cloudflare/files/internal.d.ts new file mode 100644 index 000000000000..6c79569f7f7f --- /dev/null +++ b/packages/adapter-cloudflare/files/internal.d.ts @@ -0,0 +1,12 @@ +declare module 'SERVER' { + export { Server } from '@sveltejs/kit'; +} + +declare module 'MANIFEST' { + import { SSRManifest } from '@sveltejs/kit'; + + export const manifest: SSRManifest; + export const prerendered: Set; + export const app_path: string; + export const base_path: string; +} diff --git a/packages/adapter-cloudflare/files/tsconfig.json b/packages/adapter-cloudflare/files/tsconfig.json new file mode 100644 index 000000000000..076e5e03478a --- /dev/null +++ b/packages/adapter-cloudflare/files/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": ["@cloudflare/workers-types"], + // worker types are full of typescript errors we can't fix + "skipLibCheck": true + }, + "include": ["worker.js", "internal.d.ts"] +} diff --git a/packages/adapter-cloudflare/src/worker.js b/packages/adapter-cloudflare/files/worker.js similarity index 53% rename from packages/adapter-cloudflare/src/worker.js rename to packages/adapter-cloudflare/files/worker.js index bcee34b882e2..911af0cfb971 100644 --- a/packages/adapter-cloudflare/src/worker.js +++ b/packages/adapter-cloudflare/files/worker.js @@ -1,7 +1,6 @@ import { Server } from 'SERVER'; import { manifest, prerendered, base_path } from 'MANIFEST'; import { env } from 'cloudflare:workers'; -import * as Cache from 'worktop/cfw.cache'; const server = new Server(manifest); @@ -18,13 +17,11 @@ const version_file = `${app_path}/version.json`; let origin; const initialized = server.init({ - // @ts-expect-error env contains environment variables and bindings - env, + env: /** @type {Record} */ (env), read: async (file) => { const url = `${origin}/${file}`; - const response = await /** @type {{ ASSETS: { fetch: typeof fetch } }} */ (env).ASSETS.fetch( - url - ); + const response = + await /** @type {{ ASSETS: { fetch: typeof fetch } }} */ (env).ASSETS.fetch(url); if (!response.ok) { throw new Error( @@ -51,11 +48,6 @@ export default { // always await initialization to prevent race condition with concurrent requests await initialized; - // skip cache if "cache-control: no-cache" in request - let pragma = req.headers.get('cache-control') || ''; - let res = !pragma.includes('no-cache') && (await Cache.lookup(req)); - if (res) return res; - let { pathname, search } = new URL(req.url); try { pathname = decodeURIComponent(pathname); @@ -70,10 +62,7 @@ export default { const filename = stripped_pathname.slice(base_path.length + 1); if (filename) { is_static_asset = - manifest.assets.has(filename) || - manifest.assets.has(filename + '/index.html') || - filename in manifest._.server_assets || - filename + '/index.html' in manifest._.server_assets; + manifest.assets.has(filename) || manifest.assets.has(filename + '/index.html'); } let location = pathname.at(-1) === '/' ? stripped_pathname : pathname + '/'; @@ -84,37 +73,38 @@ export default { pathname === version_file || pathname.startsWith(immutable) ) { - res = await env.ASSETS.fetch(req); - } else if (location && prerendered.has(location)) { - // trailing slash redirect for prerendered pages + let res = await env.ASSETS.fetch(req); + // `_headers` applies cache regardless of status, so we need to ensure an + // error response does not get cached + if (res.status >= 400) { + res = new Response(res.body, res); // headers from ASSETS are immutable. see https://developers.cloudflare.com/workers/examples/alter-headers/ + res.headers.set('cache-control', 'no-store'); + } + return res; + } + + // trailing slash redirect for prerendered pages + if (location && prerendered.has(location)) { if (search) location += search; - res = new Response('', { + return new Response('', { status: 308, headers: { location } }); - } else { - // dynamically-generated pages - res = await server.respond(req, { - platform: { - env, - ctx, - context: ctx, // deprecated in favor of ctx - // @ts-expect-error webworker types from worktop are not compatible with Cloudflare Workers types - caches, - // @ts-expect-error the type is correct but ts is confused because platform.cf uses the type from index.ts while req.cf uses the type from index.d.ts - cf: req.cf - }, - getClientAddress() { - return /** @type {string} */ (req.headers.get('cf-connecting-ip')); - } - }); } - // write to `Cache` only if response is not an error, - // let `Cache.save` handle the Cache-Control and Vary headers - pragma = res.headers.get('cache-control') || ''; - return pragma && res.status < 400 ? Cache.save(req, res, ctx) : res; + // dynamically-generated pages + return await server.respond(req, { + platform: { + env, + ctx, + caches, + cf: req.cf + }, + getClientAddress() { + return /** @type {string} */ (req.headers.get('cf-connecting-ip')); + } + }); } }; diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index 24e2dd503f07..d137538e7194 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -1,5 +1,4 @@ -import { VERSION } from '@sveltejs/kit'; -import { copyFileSync, existsSync, readFileSync, writeFileSync } from 'node:fs'; +import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; import path from 'node:path'; import process from 'node:process'; import { fileURLToPath } from 'node:url'; @@ -8,36 +7,32 @@ import { is_building_for_cloudflare_pages, validate_worker_settings, get_routes_json, - parse_redirects + parse_redirects, + append_headers } from './utils.js'; const name = '@sveltejs/adapter-cloudflare'; -const [kit_major, kit_minor] = VERSION.split('.'); -/** @type {import('./index.js').default} */ +/** @type {typeof import('./index.js').default} */ export default function (options = {}) { return { name, - /** @param {Builder2_0_0} builder */ async adapt(builder) { - if ( - existsSync('_routes.json') || - existsSync(`${builder.config.kit.files.assets}/_routes.json`) - ) { + if (existsSync('_routes.json') || existsSync(`${builder.config.files.assets}/_routes.json`)) { throw new Error( - "Cloudflare Pages' _routes.json should be configured in svelte.config.js. See https://svelte.dev/docs/kit/adapter-cloudflare#Options-routes" + "Cloudflare Pages' _routes.json should be configured from the adapter option of the SvelteKit plugin in your vite.config.js. See https://svelte.dev/docs/kit/adapter-cloudflare#Options-routes" ); } - if (existsSync(`${builder.config.kit.files.assets}/_headers`)) { + if (existsSync(`${builder.config.files.assets}/_headers`)) { throw new Error( - `The _headers file should be placed in the project root rather than the ${builder.config.kit.files.assets} directory` + `The _headers file should be placed in the project root rather than the ${builder.config.files.assets} directory` ); } - if (existsSync(`${builder.config.kit.files.assets}/_redirects`)) { + if (existsSync(`${builder.config.files.assets}/_redirects`)) { throw new Error( - `The _redirects file should be placed in the project root rather than the ${builder.config.kit.files.assets} directory` + `The _redirects file should be placed in the project root rather than the ${builder.config.files.assets} directory` ); } @@ -74,15 +69,15 @@ export default function (options = {}) { const files = fileURLToPath(new URL('./files', import.meta.url).href); const tmp = builder.getBuildDirectory('cloudflare-tmp'); - builder.rimraf(dest); - builder.rimraf(worker_dest); + rmSync(dest, { force: true, recursive: true }); + rmSync(worker_dest, { force: true, recursive: true }); - builder.mkdirp(dest); - builder.mkdirp(tmp); + mkdirSync(dest, { recursive: true }); + mkdirSync(tmp, { recursive: true }); // client assets and prerendered pages - const assets_dest = `${dest}${builder.config.kit.paths.base}`; - builder.mkdirp(assets_dest); + const assets_dest = `${dest}${builder.config.paths.base}`; + mkdirSync(assets_dest, { recursive: true }); if ( building_for_cloudflare_pages || wrangler_config.assets?.not_found_handling === '404-page' @@ -112,7 +107,7 @@ export default function (options = {}) { `${tmp}/manifest.js`, `export const manifest = ${builder.generateManifest({ relativePath: path.posix.relative(tmp, builder.getServerDirectory()) })};\n\n` + `export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});\n\n` + - `export const base_path = ${JSON.stringify(builder.config.kit.paths.base)};\n` + `export const base_path = ${JSON.stringify(builder.config.paths.base)};\n` ); builder.copy(`${files}/worker.js`, worker_dest, { replace: { @@ -126,8 +121,8 @@ export default function (options = {}) { ASSETS: assets_binding } }); - if (builder.hasServerInstrumentationFile?.()) { - builder.instrument?.({ + if (builder.hasServerInstrumentationFile()) { + builder.instrument({ entrypoint: worker_dest, instrumentation: `${builder.getServerDirectory()}/instrumentation.server.js` }); @@ -136,10 +131,12 @@ export default function (options = {}) { // _headers const headers_src = '_headers'; const headers_dest = `${dest}/_headers`; + /** @type {string | undefined} */ + let headers; if (existsSync(headers_src)) { - copyFileSync(headers_src, headers_dest); + headers = readFileSync(headers_src, 'utf-8'); } - writeFileSync(headers_dest, generate_headers(builder.getAppPath()), { flag: 'a' }); + writeFileSync(headers_dest, generate_headers(builder.getAppPath(), headers)); // _redirects const redirects_src = '_redirects'; @@ -213,16 +210,7 @@ export default function (options = {}) { }; }, supports: { - read: ({ route }) => { - // TODO bump peer dep in next adapter major to simplify this - if (kit_major === '2' && kit_minor < '25') { - throw new Error( - `${name}: Cannot use \`read\` from \`$app/server\` in route \`${route.id}\` when using SvelteKit < 2.25.0` - ); - } - - return true; - }, + read: () => true, instrumentation: () => true } }; @@ -230,19 +218,23 @@ export default function (options = {}) { /** * @param {string} app_dir + * @param {string | undefined} content existing `_headers` file content * @returns {string} */ -function generate_headers(app_dir) { - return ` -# === START AUTOGENERATED SVELTE IMMUTABLE HEADERS === -/${app_dir}/* - X-Robots-Tag: noindex - Cache-Control: no-cache -/${app_dir}/immutable/* - ! Cache-Control - Cache-Control: public, immutable, max-age=31536000 -# === END AUTOGENERATED SVELTE IMMUTABLE HEADERS === -`.trimEnd(); +function generate_headers(app_dir, content = '') { + content = append_headers( + `/${app_dir}/*`, + ['X-Robots-Tag: noindex', 'Cache-Control: no-cache'], + content + ); + + content = append_headers( + `/${app_dir}/immutable/*`, + ['! Cache-Control', 'Cache-Control: public, immutable, max-age=31536000'], + content + ); + + return content; } /** diff --git a/packages/adapter-cloudflare/internal.d.ts b/packages/adapter-cloudflare/internal.d.ts deleted file mode 100644 index 848182720e5d..000000000000 --- a/packages/adapter-cloudflare/internal.d.ts +++ /dev/null @@ -1,41 +0,0 @@ -declare module 'SERVER' { - export { Server } from '@sveltejs/kit'; -} - -declare module 'MANIFEST' { - import { SSRManifest } from '@sveltejs/kit'; - - export const manifest: SSRManifest; - export const prerendered: Set; - export const app_path: string; - export const base_path: string; -} - -type PartialExcept = Partial> & Required>; - -/** - * We use a custom `Builder` type here to ensure compatibility with the minimum version of SvelteKit. - */ -type Builder2_0_0 = PartialExcept< - import('@sveltejs/kit').Builder, - | 'log' - | 'rimraf' - | 'mkdirp' - | 'config' - | 'prerendered' - | 'routes' - | 'createEntries' - | 'generateFallback' - | 'generateEnvModule' - | 'generateManifest' - | 'getBuildDirectory' - | 'getClientDirectory' - | 'getServerDirectory' - | 'getAppPath' - | 'writeClient' - | 'writePrerendered' - | 'writePrerendered' - | 'writeServer' - | 'copy' - | 'compress' ->; diff --git a/packages/adapter-cloudflare/package.json b/packages/adapter-cloudflare/package.json index ef8c0fda45c1..4ad741a03261 100644 --- a/packages/adapter-cloudflare/package.json +++ b/packages/adapter-cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "@sveltejs/adapter-cloudflare", - "version": "7.2.9", + "version": "8.0.0-next.6", "description": "Adapts SvelteKit applications for Cloudflare Workers or Cloudflare Pages", "keywords": [ "adapter", @@ -27,36 +27,32 @@ }, "types": "index.d.ts", "files": [ - "files", + "files/worker.js", "index.js", "utils.js", "index.d.ts", "ambient.d.ts" ], "scripts": { - "build": "esbuild src/worker.js --bundle --outfile=files/worker.js --external:SERVER --external:MANIFEST --external:cloudflare:workers --format=esm", - "lint": "prettier --check .", - "format": "pnpm lint --write", - "check": "tsc --skipLibCheck", + "lint": "oxfmt --check .", + "format": "oxfmt --write .", + "check": "tsc && tsc --project files/tsconfig.json", "test:unit": "vitest run", - "test:e2e": "pnpm build && pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test", - "test": "pnpm test:unit && pnpm test:e2e", - "prepublishOnly": "pnpm build" + "test:e2e": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test", + "test": "pnpm test:unit && pnpm test:e2e" }, "dependencies": { - "@cloudflare/workers-types": "^4.20250507.0", - "worktop": "0.8.0-next.18" + "@cloudflare/workers-types": "^5.20260809.1" }, "devDependencies": { "@playwright/test": "catalog:", "@sveltejs/kit": "workspace:^", "@types/node": "catalog:", - "esbuild": "catalog:", - "typescript": "catalog:", + "typescript": "catalog:typescript-native", "vitest": "catalog:" }, "peerDependencies": { - "@sveltejs/kit": "^2.0.0", - "wrangler": "^4.0.0" + "@sveltejs/kit": "^3.0.0-next.0", + "wrangler": "^4.118.0" } } diff --git a/packages/adapter-cloudflare/test/apps/pages/package.json b/packages/adapter-cloudflare/test/apps/pages/package.json index 0839ddd0a69f..3e758b7a59e1 100644 --- a/packages/adapter-cloudflare/test/apps/pages/package.json +++ b/packages/adapter-cloudflare/test/apps/pages/package.json @@ -5,8 +5,7 @@ "scripts": { "dev": "vite dev", "build": "vite build", - "preview": "wrangler pages dev .svelte-kit/cloudflare --port 8787", - "prepare": "svelte-kit sync || echo ''", + "preview": "wrangler pages dev .svelte-kit/cloudflare --port 8787 --compatibility-date 2026-06-24", "test": "playwright test" }, "devDependencies": { diff --git a/packages/adapter-cloudflare/test/apps/pages/svelte.config.js b/packages/adapter-cloudflare/test/apps/pages/svelte.config.js deleted file mode 100644 index 20cd2b3ff5b8..000000000000 --- a/packages/adapter-cloudflare/test/apps/pages/svelte.config.js +++ /dev/null @@ -1,10 +0,0 @@ -import adapter from '../../../index.js'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter() - } -}; - -export default config; diff --git a/packages/adapter-cloudflare/test/apps/pages/tsconfig.json b/packages/adapter-cloudflare/test/apps/pages/tsconfig.json index c2db702f9bb1..0c2092500732 100644 --- a/packages/adapter-cloudflare/test/apps/pages/tsconfig.json +++ b/packages/adapter-cloudflare/test/apps/pages/tsconfig.json @@ -9,5 +9,5 @@ "sourceMap": true, "moduleResolution": "bundler" }, - "extends": "./.svelte-kit/tsconfig.json" + "extends": "$app/tsconfig" } diff --git a/packages/adapter-cloudflare/test/apps/pages/vite.config.js b/packages/adapter-cloudflare/test/apps/pages/vite.config.js index 29ad08debe6a..01b9947e7d2a 100644 --- a/packages/adapter-cloudflare/test/apps/pages/vite.config.js +++ b/packages/adapter-cloudflare/test/apps/pages/vite.config.js @@ -1,11 +1,12 @@ import { sveltekit } from '@sveltejs/kit/vite'; +import adapter from '../../../index.js'; /** @type {import('vite').UserConfig} */ const config = { build: { minify: false }, - plugins: [sveltekit()] + plugins: [sveltekit({ adapter: adapter() })] }; export default config; diff --git a/packages/adapter-cloudflare/test/apps/workers/.gitignore b/packages/adapter-cloudflare/test/apps/workers/.gitignore index f9ee23cc8621..de2e15da78f4 100644 --- a/packages/adapter-cloudflare/test/apps/workers/.gitignore +++ b/packages/adapter-cloudflare/test/apps/workers/.gitignore @@ -1,5 +1,7 @@ .DS_Store node_modules /.svelte-kit -/.wrangler -/dist \ No newline at end of file + +# Cloudflare +.wrangler +/dist diff --git a/packages/adapter-cloudflare/test/apps/workers/config/wrangler.jsonc b/packages/adapter-cloudflare/test/apps/workers/config/wrangler.jsonc index 0bf8a9db8c3a..8c2d83f8c21a 100644 --- a/packages/adapter-cloudflare/test/apps/workers/config/wrangler.jsonc +++ b/packages/adapter-cloudflare/test/apps/workers/config/wrangler.jsonc @@ -3,6 +3,7 @@ { "$schema": "../node_modules/wrangler/config-schema.json", "main": "../dist/index.js", + "compatibility_date": "2026-06-24", "assets": { "directory": "../dist/public", "binding": "ASSETS" diff --git a/packages/adapter-cloudflare/test/apps/workers/package.json b/packages/adapter-cloudflare/test/apps/workers/package.json index 4032805d1ec6..c59a5adc2774 100644 --- a/packages/adapter-cloudflare/test/apps/workers/package.json +++ b/packages/adapter-cloudflare/test/apps/workers/package.json @@ -5,8 +5,7 @@ "scripts": { "dev": "vite dev", "build": "vite build", - "preview": "wrangler dev dist/index.js", - "prepare": "svelte-kit sync || echo ''", + "preview": "wrangler dev dist/index.js --config config/wrangler.jsonc", "test:dev": "DEV=true playwright test", "test:build": "playwright test", "test": "pnpm test:dev && pnpm test:build" diff --git a/packages/adapter-cloudflare/test/apps/workers/src/app.d.ts b/packages/adapter-cloudflare/test/apps/workers/src/app.d.ts deleted file mode 100644 index 101a9db0b8bf..000000000000 --- a/packages/adapter-cloudflare/test/apps/workers/src/app.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -// TODO: remove this in 3.0 once svelte.config.js is included by the generated tsconfig.json -// this ensures we get the ambient types from the adapter -import '../../../../index.js'; diff --git a/packages/adapter-cloudflare/test/apps/workers/svelte.config.js b/packages/adapter-cloudflare/test/apps/workers/svelte.config.js deleted file mode 100644 index 26cd6a965908..000000000000 --- a/packages/adapter-cloudflare/test/apps/workers/svelte.config.js +++ /dev/null @@ -1,12 +0,0 @@ -import adapter from '../../../index.js'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter({ - config: 'config/wrangler.jsonc' - }) - } -}; - -export default config; diff --git a/packages/adapter-cloudflare/test/apps/workers/test/test.js b/packages/adapter-cloudflare/test/apps/workers/test/test.js index 40072953a16f..44832c70890f 100644 --- a/packages/adapter-cloudflare/test/apps/workers/test/test.js +++ b/packages/adapter-cloudflare/test/apps/workers/test/test.js @@ -1,10 +1,7 @@ import fs from 'node:fs'; import path from 'node:path'; -import { fileURLToPath } from 'node:url'; import { expect, test } from '@playwright/test'; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - test('worker', async ({ page }) => { await page.goto('/'); await expect(page.locator('h1')).toContainText('Sum: 3'); @@ -16,7 +13,10 @@ test('ctx', async ({ request }) => { }); test('read from $app/server works', async ({ request }) => { - const content = fs.readFileSync(path.resolve(__dirname, '../src/routes/read/file.txt'), 'utf-8'); + const content = fs.readFileSync( + path.resolve(import.meta.dirname, '../src/routes/read/file.txt'), + 'utf-8' + ); const response = await request.get('/read'); expect(await response.text()).toBe(content); }); diff --git a/packages/adapter-cloudflare/test/apps/workers/tsconfig.json b/packages/adapter-cloudflare/test/apps/workers/tsconfig.json index c2db702f9bb1..0c2092500732 100644 --- a/packages/adapter-cloudflare/test/apps/workers/tsconfig.json +++ b/packages/adapter-cloudflare/test/apps/workers/tsconfig.json @@ -9,5 +9,5 @@ "sourceMap": true, "moduleResolution": "bundler" }, - "extends": "./.svelte-kit/tsconfig.json" + "extends": "$app/tsconfig" } diff --git a/packages/adapter-cloudflare/test/apps/workers/vite.config.js b/packages/adapter-cloudflare/test/apps/workers/vite.config.js index 29ad08debe6a..195f6c80776e 100644 --- a/packages/adapter-cloudflare/test/apps/workers/vite.config.js +++ b/packages/adapter-cloudflare/test/apps/workers/vite.config.js @@ -1,11 +1,18 @@ import { sveltekit } from '@sveltejs/kit/vite'; +import adapter from '../../../index.js'; /** @type {import('vite').UserConfig} */ const config = { build: { minify: false }, - plugins: [sveltekit()] + plugins: [ + sveltekit({ + adapter: adapter({ + config: 'config/wrangler.jsonc' + }) + }) + ] }; export default config; diff --git a/packages/adapter-cloudflare/test/utils.js b/packages/adapter-cloudflare/test/utils.js index dcb3ce52c9d6..c2ffc070bff8 100644 --- a/packages/adapter-cloudflare/test/utils.js +++ b/packages/adapter-cloudflare/test/utils.js @@ -21,7 +21,7 @@ export const config = { ...devices['Desktop Chrome'], screenshot: 'only-on-failure', trace: 'retain-on-failure', - channel: 'chromium' + channel: 'chrome' }, workers: process.env.CI ? 2 : number_from_env('KIT_E2E_WORKERS', undefined), reporter: 'list', diff --git a/packages/adapter-cloudflare/tsconfig.json b/packages/adapter-cloudflare/tsconfig.json index d1fdac44e7b3..ce628354cbad 100644 --- a/packages/adapter-cloudflare/tsconfig.json +++ b/packages/adapter-cloudflare/tsconfig.json @@ -3,22 +3,17 @@ "allowJs": true, "checkJs": true, "noEmit": true, - "target": "es2022", - "module": "node16", - "moduleResolution": "node16", + "module": "nodenext", + "moduleResolution": "nodenext", "paths": { "@sveltejs/kit": ["../kit/types/index"] }, + // playwright and worker types are a mess + "skipLibCheck": true, // taken from the Cloudflare Workers TypeScript template https://github.com/cloudflare/workers-sdk/blob/main/packages/create-cloudflare/templates/hello-world/ts/tsconfig.json - "lib": ["es2021"], - "types": ["node", "@cloudflare/workers-types"] + "target": "es2024", + "lib": ["es2024"], + "types": ["node"] }, - "include": [ - "index.js", - "utils.js", - "utils.spec.js", - "test/utils.js", - "internal.d.ts", - "src/worker.js" - ] + "include": ["index.js", "utils.js", "utils.spec.js", "vitest.config.js", "test/utils.js"] } diff --git a/packages/adapter-cloudflare/utils.js b/packages/adapter-cloudflare/utils.js index 28d83691b12a..82f5683c6e07 100644 --- a/packages/adapter-cloudflare/utils.js +++ b/packages/adapter-cloudflare/utils.js @@ -9,7 +9,7 @@ export function is_building_for_cloudflare_pages(wrangler_config) { return true; } - if (wrangler_config.main || wrangler_config.assets) { + if (!!process.env.WORKERS_CI || wrangler_config.main || wrangler_config.assets) { return false; } @@ -81,7 +81,7 @@ export function parse_redirects(file_contents) { /** * Generates the [_routes.json](https://developers.cloudflare.com/pages/functions/routing/#create-a-_routesjson-file) * file that dictates which routes invoke the Cloudflare Worker. - * @param {Builder2_0_0} builder + * @param {import('@sveltejs/kit').Builder} builder * @param {string[]} client_assets * @param {string[]} redirects * @param {import('./index.js').AdapterOptions['routes']} routes @@ -128,8 +128,8 @@ export function get_routes_json(builder, client_assets, redirects, routes) { if (rule === '') { for (const file of client_assets) { - if (file.startsWith(`${builder.config.kit.appDir}/`)) continue; - excluded_routes.add(`${builder.config.kit.paths.base}/${file}`); + if (file.startsWith(`${builder.config.appDir}/`)) continue; + excluded_routes.add(`${builder.config.paths.base}/${file}`); } continue; } @@ -163,3 +163,29 @@ export function get_routes_json(builder, client_assets, redirects, routes) { exclude }; } + +/** + * Adds header rules to the contents of a `_headers` file + * @param {string} url + * @param {string[]} rules + * @param {string} content + * @returns {string} + */ +export function append_headers(url, rules, content) { + const regex = new RegExp(`^(${url.replaceAll('*', '\\*')})$`, 'm'); + const formatted_headers = rules.map((rule) => ` ${rule}`).join('\n'); + + // if the URL already exists, just add header rules to it + if (regex.test(content)) { + return content.replace(regex, `$1\n${formatted_headers}`); + } + + // otherwise, we add the url and header rules + return ` +${content} +# === START AUTOGENERATED SVELTE IMMUTABLE HEADERS === +${url} +${formatted_headers} +# === END AUTOGENERATED SVELTE IMMUTABLE HEADERS === +`.trim(); +} diff --git a/packages/adapter-cloudflare/utils.spec.js b/packages/adapter-cloudflare/utils.spec.js index f6059a96609f..8b0d2af7ce04 100644 --- a/packages/adapter-cloudflare/utils.spec.js +++ b/packages/adapter-cloudflare/utils.spec.js @@ -3,7 +3,8 @@ import { is_building_for_cloudflare_pages, validate_worker_settings, get_routes_json, - parse_redirects + parse_redirects, + append_headers } from './utils.js'; describe('detects Cloudflare Pages project', () => { @@ -68,6 +69,15 @@ describe('detects Cloudflare Workers project', () => { ) ).toBe(false); }); + + test('WORKERS_CI environment variable', () => { + vi.stubEnv('WORKERS_CI', '1'); + const result = is_building_for_cloudflare_pages( + /** @type {import('wrangler').Unstable_Config} */ ({}) + ); + vi.unstubAllEnvs(); + expect(result).toBe(false); + }); }); describe('validates Wrangler config', () => { @@ -148,42 +158,82 @@ describe('validates Wrangler config', () => { }); }); -test('ignores comments in _redirects file', () => { - const redirects = parse_redirects( - ` -# This is a comment -/home301 / 301 - # Indented comment -/blog/* https://blog.my.domain/:splat +describe('_headers', () => { + test('adds URL and its headers', () => { + const content = append_headers( + '/_app/*', + ['X-Robots-Tag: noindex', 'Cache-Control: no-cache'], + '/custom\n X-Custom-Rule: value' + ); + expect(content).toEqual( + ` +/custom + X-Custom-Rule: value +# === START AUTOGENERATED SVELTE IMMUTABLE HEADERS === +/_app/* + X-Robots-Tag: noindex + Cache-Control: no-cache +# === END AUTOGENERATED SVELTE IMMUTABLE HEADERS === `.trim() - ); + ); + }); - expect(redirects).toEqual(['/home301', '/blog/*']); + test('adds headers to existing URLs', () => { + const content = append_headers( + '/_app/*', + ['X-Robots-Tag: noindex', 'Cache-Control: no-cache'], + '/_app/*\n X-Custom-Rule: value' + ); + expect(content).toEqual( + ` +/_app/* + X-Robots-Tag: noindex + Cache-Control: no-cache + X-Custom-Rule: value +`.trim() + ); + }); }); -test('parses _redirects file', () => { - const redirects = parse_redirects( - ` +describe('_redirects', () => { + test('parses sources', () => { + const redirects = parse_redirects( + ` /home301 / 301 /notrailing/ /nottrailing 301 /blog/* https://blog.my.domain/:splat `.trim() - ); + ); + + expect(redirects).toEqual(['/home301', '/notrailing/', '/blog/*']); + }); + + test('ignores comments while parsing', () => { + const redirects = parse_redirects( + ` +# This is a comment +/home301 / 301 + # Indented comment +/blog/* https://blog.my.domain/:splat +`.trim() + ); - expect(redirects).toEqual(['/home301', '/notrailing/', '/blog/*']); + expect(redirects).toEqual(['/home301', '/blog/*']); + }); }); -test('generates a _routes.json file', () => { - const routes = get_routes_json( - { - getAppPath: () => 'base-path/_app', - config: { - kit: { +describe('_routes.json', () => { + test('generates file contents', () => { + const routes = get_routes_json( + { + getAppPath: () => 'base-path/_app', + config: { appDir: '_app', paths: { base: '/base-path', assets: '', + origin: 'http://localhost', relative: true }, alias: {}, @@ -200,7 +250,6 @@ test('generates a _routes.json file', () => { server: 'src/hooks.server.js', universal: 'src/hooks.js' }, - lib: 'src/lib', params: 'src/params', routes: 'src/routes', serviceWorker: 'src/service-worker.js', @@ -217,53 +266,50 @@ test('generates a _routes.json file', () => { reportOnly: {} }, env: { - dir: '.', - publicPrefix: 'PUBLIC_', - privatePrefix: '' + dir: '.' }, outDir: '.svelte-kit' + }, + prerendered: { + paths: ['/base-path/prerendered'], + pages: new Map(), + assets: new Map(), + redirects: new Map() } }, - prerendered: { - paths: ['/base-path/prerendered'], - pages: new Map(), - assets: new Map(), - redirects: new Map() - } - }, - ['_app/immutable/this-should-not-be-excluded.js', 'robots.txt'], - ['/base-path/redirect'], - undefined - ); + ['_app/immutable/this-should-not-be-excluded.js', 'robots.txt'], + ['/base-path/redirect'], + undefined + ); - expect(routes).toEqual({ - version: 1, - description: 'Generated by @sveltejs/adapter-cloudflare', - include: ['/*'], - exclude: [ - '/base-path/_app/version.json', - '/base-path/_app/immutable/*', - '/base-path/robots.txt', - '/base-path/prerendered', - '/base-path/redirect' - ] + expect(routes).toEqual({ + version: 1, + description: 'Generated by @sveltejs/adapter-cloudflare', + include: ['/*'], + exclude: [ + '/base-path/_app/version.json', + '/base-path/_app/immutable/*', + '/base-path/robots.txt', + '/base-path/prerendered', + '/base-path/redirect' + ] + }); }); -}); -test('truncates excess _routes.json exclude rules', () => { - const routes = get_routes_json( - { - // @ts-ignore - log: { - warn: console.warn - }, - getAppPath: () => 'base-path/_app', - config: { - kit: { + test('truncates excess exclude rules', () => { + const routes = get_routes_json( + { + // @ts-ignore + log: { + warn: console.warn + }, + getAppPath: () => 'base-path/_app', + config: { appDir: '_app', paths: { base: '/base-path', assets: '', + origin: 'http://localhost', relative: true }, alias: {}, @@ -280,7 +326,6 @@ test('truncates excess _routes.json exclude rules', () => { server: 'src/hooks.server.js', universal: 'src/hooks.js' }, - lib: 'src/lib', params: 'src/params', routes: 'src/routes', serviceWorker: 'src/service-worker.js', @@ -297,33 +342,31 @@ test('truncates excess _routes.json exclude rules', () => { reportOnly: {} }, env: { - dir: '.', - publicPrefix: 'PUBLIC_', - privatePrefix: '' + dir: '.' }, outDir: '.svelte-kit' + }, + prerendered: { + paths: Array.from({ length: 100 }, (_, i) => `/base-path/blog/post/${i + 1}`), + pages: new Map(), + assets: new Map(), + redirects: new Map() } }, - prerendered: { - paths: Array.from({ length: 100 }, (_, i) => `/base-path/blog/post/${i + 1}`), - pages: new Map(), - assets: new Map(), - redirects: new Map() - } - }, - ['_app/immutable/this-should-not-be-excluded.js', 'robots.txt'], - [], - undefined - ); + ['_app/immutable/this-should-not-be-excluded.js', 'robots.txt'], + [], + undefined + ); - expect(routes).toEqual({ - version: 1, - description: 'Generated by @sveltejs/adapter-cloudflare', - include: ['/*'], - exclude: [ - '/base-path/_app/version.json', - '/base-path/_app/immutable/*', - '/base-path/robots.txt' - ].concat(Array.from({ length: 96 }, (_, i) => `/base-path/blog/post/${i + 1}`)) + expect(routes).toEqual({ + version: 1, + description: 'Generated by @sveltejs/adapter-cloudflare', + include: ['/*'], + exclude: [ + '/base-path/_app/version.json', + '/base-path/_app/immutable/*', + '/base-path/robots.txt' + ].concat(Array.from({ length: 96 }, (_, i) => `/base-path/blog/post/${i + 1}`)) + }); }); }); diff --git a/packages/adapter-cloudflare/vitest.config.js b/packages/adapter-cloudflare/vitest.config.js new file mode 100644 index 000000000000..a15b3a470a6d --- /dev/null +++ b/packages/adapter-cloudflare/vitest.config.js @@ -0,0 +1,5 @@ +// we need this file to prevent Vitest from resolving a Vitest config from another directory + +import { defineConfig } from 'vitest/config'; + +export default defineConfig({}); diff --git a/packages/adapter-netlify/.gitignore b/packages/adapter-netlify/.gitignore index 847a92e0c157..9daa8247da45 100644 --- a/packages/adapter-netlify/.gitignore +++ b/packages/adapter-netlify/.gitignore @@ -1,3 +1,2 @@ .DS_Store node_modules -/files diff --git a/packages/adapter-netlify/CHANGELOG.md b/packages/adapter-netlify/CHANGELOG.md index b88615a71b61..9623d5d621c8 100644 --- a/packages/adapter-netlify/CHANGELOG.md +++ b/packages/adapter-netlify/CHANGELOG.md @@ -1,5 +1,122 @@ # @sveltejs/adapter-netlify +## 7.0.0-next.8 + +### Patch Changes + +- chore: bump `rolldown` to 1.2.3 ([#16745](https://github.com/sveltejs/kit/pull/16745)) +- Updated dependencies [[`1742811`](https://github.com/sveltejs/kit/commit/1742811562358caf6042bed33c60ce6427a3e831), [`1611c61`](https://github.com/sveltejs/kit/commit/1611c617a1ec1b32e10627c2815fb7e6673f3157), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`13e7b18`](https://github.com/sveltejs/kit/commit/13e7b186a94b5b8317771d2b1e795b432dcd6faf), [`529346d`](https://github.com/sveltejs/kit/commit/529346d96369f92cdd804be9c4313c598efe34b8), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`81d6319`](https://github.com/sveltejs/kit/commit/81d6319eef08a765ccd3922790a7aee16e284cc2), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`69a5bdf`](https://github.com/sveltejs/kit/commit/69a5bdf9f45810eeed2faa089c8d3206463d347e)]: + - @sveltejs/kit@3.0.0-next.20 + +## 7.0.0-next.7 + +### Patch Changes + +- fix: import the generated manifest via `pathToFileURL`, which handles special characters in the project path ([#16649](https://github.com/sveltejs/kit/pull/16649)) +- Updated dependencies [[`c66e589`](https://github.com/sveltejs/kit/commit/c66e589d9ba2dba7577c2c4857c8989dd65c066d), [`0bee0e8`](https://github.com/sveltejs/kit/commit/0bee0e8f2bed771a92a3d6860363cda7c710839a), [`9703b94`](https://github.com/sveltejs/kit/commit/9703b94b51d71ffd5ebbce8229d35695db790f15)]: + - @sveltejs/kit@3.0.0-next.17 + +## 7.0.0-next.6 + +### Major Changes + +- chore: use `rolldown` for edge function bundling + +- breaking: write output that conforms to the stable [Netlify Frameworks API](https://docs.netlify.com/build/frameworks/frameworks-api/). + + Deploying and previewing with Netlify CLI now requires [v17.31.0](https://github.com/netlify/cli/releases/tag/v17.31.0) or later. Run `npm i -g netlify-cli@latest` to upgrade. +- breaking: require SvelteKit 3 + +- breaking: edge function build target is now `es2022` + +- breaking: require `vite@^8.0.12`, the first Vite 8 release bundling stable `rolldown` 1.0.0 + +### Patch Changes + +- chore: use `node:fs` instead of deprecated `builder.rimraf` and `builder.mkdirp` ([#16286](https://github.com/sveltejs/kit/pull/16286)) + +- fix: ensure types for `platform.context` work + +- fix: include `utils.js` in package.json `files` + +- chore: replace @netlify/functions with @netlify/types for the Context type + +- fix: provide distinct names for each split serverless function ([#16667](https://github.com/sveltejs/kit/pull/16667)) + +- fix: await `init` on every request to prevent race condition + +- chore: bump Rolldown to `1.2.0` +- Updated dependencies [[`bbd1e2f`](https://github.com/sveltejs/kit/commit/bbd1e2fcc9af58edb9846393b081be61df4789d5), [`e21d27b`](https://github.com/sveltejs/kit/commit/e21d27ba490c89da98dbad24fca364d0163137c4), [`6044d05`](https://github.com/sveltejs/kit/commit/6044d055031bc17deedc069dc4016565364bd4f4), [`7fb611f`](https://github.com/sveltejs/kit/commit/7fb611fb604e4d74cd889f5fe21ccd255f876382), [`9f06b3a`](https://github.com/sveltejs/kit/commit/9f06b3ac78be30d27b07fc00ea40bda493c573aa), [`8763327`](https://github.com/sveltejs/kit/commit/87633271ce40ac0839be9468598e965bde3cad51), [`7dc2379`](https://github.com/sveltejs/kit/commit/7dc23798cde97b5616049ba8d443a07e8c558bdc), [`a70c3eb`](https://github.com/sveltejs/kit/commit/a70c3eb31fe1e28f1532c84ec569ec7099da3344), [`89f49c7`](https://github.com/sveltejs/kit/commit/89f49c70b7fb4883cf704d2fb57d2d8595790ad2), [`f36b3a4`](https://github.com/sveltejs/kit/commit/f36b3a4cd307bd8c89e03fa191becc175e7b3077), [`1144a63`](https://github.com/sveltejs/kit/commit/1144a63d318c3c3255d06b374bb7a656a469fabf), [`9f06b3a`](https://github.com/sveltejs/kit/commit/9f06b3ac78be30d27b07fc00ea40bda493c573aa)]: + - @sveltejs/kit@3.0.0-next.15 + +## 7.0.0-next.5 + +### Patch Changes + +- chore: replace @netlify/functions with @netlify/types for the Context type ([#16542](https://github.com/sveltejs/kit/pull/16542)) +- Updated dependencies [[`6942ddb`](https://github.com/sveltejs/kit/commit/6942ddb68b43b5451337a8b420ce05a7a5de9919), [`68137d4`](https://github.com/sveltejs/kit/commit/68137d48cbf921264c6325ffa940165563c0b25a), [`15dc603`](https://github.com/sveltejs/kit/commit/15dc603f5027ee21b261c0c93f7decbf8f9498d8), [`a4f1521`](https://github.com/sveltejs/kit/commit/a4f15213c46f34aac88d05eb9af0905a5f776353), [`629b45d`](https://github.com/sveltejs/kit/commit/629b45d9e68846d862c10006fdabcc1526849811), [`7a7833a`](https://github.com/sveltejs/kit/commit/7a7833af90c0fa70c926cc84d61ca03f9db97bd2), [`e6591a0`](https://github.com/sveltejs/kit/commit/e6591a02743a2b324aa30cd463620450264fabae), [`7a7833a`](https://github.com/sveltejs/kit/commit/7a7833af90c0fa70c926cc84d61ca03f9db97bd2), [`b196a39`](https://github.com/sveltejs/kit/commit/b196a3965dd892a612cc6eae4868e450cc325736), [`66bf343`](https://github.com/sveltejs/kit/commit/66bf343453f8babf719193629a36f084d7bddf75), [`4bde76b`](https://github.com/sveltejs/kit/commit/4bde76b8a7ffa6885878d74eab3e542f1bda9c80), [`d9f05cb`](https://github.com/sveltejs/kit/commit/d9f05cb8efd8682bf91635f517f402849570cb20), [`40efca1`](https://github.com/sveltejs/kit/commit/40efca1b4afd6f0b6329bed652025b99edc8978c), [`8ef5548`](https://github.com/sveltejs/kit/commit/8ef5548f3e63ebb8371099a91acc182f7d094557), [`9efb8f2`](https://github.com/sveltejs/kit/commit/9efb8f2440328a536ebbb70a9e36ce4e30815e8a), [`6a670dc`](https://github.com/sveltejs/kit/commit/6a670dc778561e1fc504c2ccce864a46c2027dcf), [`3416dcc`](https://github.com/sveltejs/kit/commit/3416dccdceddf70ad229b5a1963d0735a7146c01), [`6850fa5`](https://github.com/sveltejs/kit/commit/6850fa56841afc8b1178fe16b6e61ffdfc854ab2), [`e6591a0`](https://github.com/sveltejs/kit/commit/e6591a02743a2b324aa30cd463620450264fabae), [`688bead`](https://github.com/sveltejs/kit/commit/688bead796f60db2384174e4f229c1d558a9ef00), [`b596610`](https://github.com/sveltejs/kit/commit/b596610a0c2e31e7d9adbdfb8d1354a165b5fe23), [`0f26c13`](https://github.com/sveltejs/kit/commit/0f26c133d3c5c19ed375f097b1eb27f6ce1928cd), [`110a3db`](https://github.com/sveltejs/kit/commit/110a3dbdba5f668d3e51b5e9f0d64be5a9ae5b39), [`665d467`](https://github.com/sveltejs/kit/commit/665d46720d26f545cf5ff52c86e44c9b2aa2521b), [`9c8c60c`](https://github.com/sveltejs/kit/commit/9c8c60c26c44e0705091de30a9a485b39d593def)]: + - @sveltejs/kit@3.0.0-next.13 + +## 7.0.0-next.4 + +### Patch Changes + +- fix: await `init` on every request to prevent race condition ([#16467](https://github.com/sveltejs/kit/pull/16467)) + +- chore: bump Rolldown to `1.2.0` ([#16455](https://github.com/sveltejs/kit/pull/16455)) +- Updated dependencies [[`adc4c5b`](https://github.com/sveltejs/kit/commit/adc4c5be542ecf7da93ee8d0e90e9d13b952998b), [`ecb0701`](https://github.com/sveltejs/kit/commit/ecb0701b8826e41229f84922f37c12099c66d3bc), [`4b4cc60`](https://github.com/sveltejs/kit/commit/4b4cc60990fa940236639a6cecb6c8efe4287bfa), [`7390dbe`](https://github.com/sveltejs/kit/commit/7390dbe6ef8dff0a1cd923f96daef462a3ce68f2), [`2e71340`](https://github.com/sveltejs/kit/commit/2e713407deb22965777afb01cb8215013ef8bfd3), [`c87bc3a`](https://github.com/sveltejs/kit/commit/c87bc3ac7830f8ee4e76254c9315adcc79ff6a99), [`af15c6c`](https://github.com/sveltejs/kit/commit/af15c6cab2fbfe6bae51dc1fa77a9271df996a47), [`c6fa431`](https://github.com/sveltejs/kit/commit/c6fa43157ae0dbaa9fc61b7e8d2db673e1f9f893), [`bfe4dea`](https://github.com/sveltejs/kit/commit/bfe4deae006f61347b5a5dce7747ec96692e97f9), [`df7dc72`](https://github.com/sveltejs/kit/commit/df7dc7219451bcc86a93e93188d78d664973d9ae), [`5ae11a1`](https://github.com/sveltejs/kit/commit/5ae11a119667c1b24e3b6d17f4b538032ddf1ab1), [`781205c`](https://github.com/sveltejs/kit/commit/781205c41cc5696eb862e5c13356c301a0368983), [`c87bc3a`](https://github.com/sveltejs/kit/commit/c87bc3ac7830f8ee4e76254c9315adcc79ff6a99), [`a1bfeb9`](https://github.com/sveltejs/kit/commit/a1bfeb9de906ae140b9cdc7d7eeaf0ee652a7bcc), [`c87bc3a`](https://github.com/sveltejs/kit/commit/c87bc3ac7830f8ee4e76254c9315adcc79ff6a99), [`9f0127d`](https://github.com/sveltejs/kit/commit/9f0127d4eee3f17dc1db1d2b99b1ca5fc80af784), [`4b4cc60`](https://github.com/sveltejs/kit/commit/4b4cc60990fa940236639a6cecb6c8efe4287bfa), [`8f5b9c7`](https://github.com/sveltejs/kit/commit/8f5b9c72ae930b3b93ef9e1b9ecb9e52664e1d74), [`c9b5544`](https://github.com/sveltejs/kit/commit/c9b554448633e1839e8c0f16c374b00a5ce4d1b6), [`fe1d4a4`](https://github.com/sveltejs/kit/commit/fe1d4a4d68d1d7a3925004fe804209ff54091e34), [`4b4cc60`](https://github.com/sveltejs/kit/commit/4b4cc60990fa940236639a6cecb6c8efe4287bfa), [`5f78e95`](https://github.com/sveltejs/kit/commit/5f78e953ae3966c23a01194dd200f5c57f723e40), [`25510fc`](https://github.com/sveltejs/kit/commit/25510fc79484363277d062e01f2b872f17a473f5), [`c99a6cf`](https://github.com/sveltejs/kit/commit/c99a6cfb7fec133aa85a6de59b43fc442aedc41a), [`17a45ca`](https://github.com/sveltejs/kit/commit/17a45cadb9356403b82c170f0d0027a1ba085667), [`2ca20c3`](https://github.com/sveltejs/kit/commit/2ca20c3ee7348836757dc6f3469217e46957766b), [`eb5c973`](https://github.com/sveltejs/kit/commit/eb5c9737b6d74c53f572b3e047faa5947052ced2)]: + - @sveltejs/kit@3.0.0-next.12 + +## 7.0.0-next.3 + +### Patch Changes + +- fix: include `utils.js` in package.json `files` ([#16298](https://github.com/sveltejs/kit/pull/16298)) +- Updated dependencies [[`737d119`](https://github.com/sveltejs/kit/commit/737d119730ec163832f838b3813c9cbb75dd799f), [`fa78efb`](https://github.com/sveltejs/kit/commit/fa78efb626031497eecc3a88fa7b6f6da36eca0f), [`07c207e`](https://github.com/sveltejs/kit/commit/07c207ed919570d4ed4954f751056b4f838abdea), [`a47071b`](https://github.com/sveltejs/kit/commit/a47071bb6581fe5f6df4cc47b80e9f0f594624e5), [`14d7d5a`](https://github.com/sveltejs/kit/commit/14d7d5abd9cd8462e4c79ed6a5c10bc43258e97a), [`5c38e51`](https://github.com/sveltejs/kit/commit/5c38e515db7fbb92e5ae01db84b4f0040a02f187), [`0702baa`](https://github.com/sveltejs/kit/commit/0702baa58b2a4600e83b55ef4a85e9cbe3391a4f), [`e1938c6`](https://github.com/sveltejs/kit/commit/e1938c62348b8ccc83e186b1acad76a0a10cc9ea), [`8293144`](https://github.com/sveltejs/kit/commit/8293144b6fbfff9f97cdb1d2224a5eaa04c84fc4), [`f76d7d9`](https://github.com/sveltejs/kit/commit/f76d7d91e18a0afd1f6798c095eb8c170c108cff), [`ab5c253`](https://github.com/sveltejs/kit/commit/ab5c253efb2546ead74db4c8c9b4c7639d97e0ed), [`b557b1b`](https://github.com/sveltejs/kit/commit/b557b1ba76903e1fb0fc3bb87bddc5420332137a), [`4a513e2`](https://github.com/sveltejs/kit/commit/4a513e27ab2ad44dae0c6a4a6c8deda840008530)]: + - @sveltejs/kit@3.0.0-next.8 + +## 7.0.0-next.2 + +### Patch Changes + +- fix: ensure types for `platform.context` work ([#16255](https://github.com/sveltejs/kit/pull/16255)) +- Updated dependencies [[`ba78a0b`](https://github.com/sveltejs/kit/commit/ba78a0bcc4ada3c649cb5d077d0a94c78045b389), [`a248c9b`](https://github.com/sveltejs/kit/commit/a248c9b6bd0593d0d28b7193fc14071944214dc3), [`7596981`](https://github.com/sveltejs/kit/commit/7596981950f534c61a0d23e73ec5dd8d5679d95c), [`be72ed9`](https://github.com/sveltejs/kit/commit/be72ed98cddf240af56d8595e4809cfbd478eec0), [`a5bd7e2`](https://github.com/sveltejs/kit/commit/a5bd7e22e94def2061f3dff9f7ff41f445cdb7a6), [`dec671f`](https://github.com/sveltejs/kit/commit/dec671f6634570053a1439a62440f2edb3044aeb), [`82f3867`](https://github.com/sveltejs/kit/commit/82f38673765e5b561da70fca25e7a4e9e5fec33b), [`97eb324`](https://github.com/sveltejs/kit/commit/97eb3247fc17a8ec6d2acfe854397eb6fe667617), [`e2a9ac0`](https://github.com/sveltejs/kit/commit/e2a9ac01ce9953eb45193f33cf31e203bb2f3656), [`309bfb4`](https://github.com/sveltejs/kit/commit/309bfb4da61b0d97623e17d4e5fdd70dbd4f970e), [`522a86b`](https://github.com/sveltejs/kit/commit/522a86b7b57099b407d213b558a1733583b9811c), [`0ff547f`](https://github.com/sveltejs/kit/commit/0ff547fe596a00b1d67f3b68d57f18a4a8f003da), [`3d4ff91`](https://github.com/sveltejs/kit/commit/3d4ff91cb7c948270ddc3e206413dd6d2da93b16), [`c925f2a`](https://github.com/sveltejs/kit/commit/c925f2a323dca6c7206e8ddd2c516f30bb89e6f7), [`7daf445`](https://github.com/sveltejs/kit/commit/7daf44551b6540244900a281a7bddd31241d65f3)]: + - @sveltejs/kit@3.0.0-next.7 + +## 7.0.0-next.1 +### Major Changes + + +- breaking: require `vite@^8.0.12`, the first Vite 8 release bundling stable `rolldown` 1.0.0 ([#16134](https://github.com/sveltejs/kit/pull/16134)) + + +### Patch Changes + +- Updated dependencies [[`a9629f1`](https://github.com/sveltejs/kit/commit/a9629f136d3256b14a8b3c10114c99832b977f6c), [`53d37f9`](https://github.com/sveltejs/kit/commit/53d37f93cf1f46995264398d4e54bb27bd1e75dd), [`dba6239`](https://github.com/sveltejs/kit/commit/dba62390e96f9f40c55ddc203744138664742026), [`ab12cb6`](https://github.com/sveltejs/kit/commit/ab12cb6a07269e2a18c0a3867e608e9838c592f3), [`f0eab52`](https://github.com/sveltejs/kit/commit/f0eab522bdea2d2a00f9b5473cd917d678b20daa), [`cd884c1`](https://github.com/sveltejs/kit/commit/cd884c1ffa6f96e08c70486c52fcca20501d5cef), [`9ed38a8`](https://github.com/sveltejs/kit/commit/9ed38a886be8c247c42c887c15d53b405693e1a9), [`53aa049`](https://github.com/sveltejs/kit/commit/53aa049a06ce7b053bdbe6484f05eecdb09d874e), [`984e57a`](https://github.com/sveltejs/kit/commit/984e57ac95d6ca7911611e1c297eecd1b1d870db), [`51785be`](https://github.com/sveltejs/kit/commit/51785be17ac706659d7fd7b3fca3fcf58dcc5de0), [`4f6bcbb`](https://github.com/sveltejs/kit/commit/4f6bcbbbb6e104238c41172ce7dbdaba486bbf43), [`78e7137`](https://github.com/sveltejs/kit/commit/78e71372ca284160df0aefe407999d46795a1299)]: + - @sveltejs/kit@3.0.0-next.5 + +## 7.0.0-next.0 +### Major Changes + + +- chore: use `rolldown` for edge function bundling ([#15432](https://github.com/sveltejs/kit/pull/15432)) + + +- breaking: write output that conforms to the stable [Netlify Frameworks API](https://docs.netlify.com/build/frameworks/frameworks-api/). ([#15294](https://github.com/sveltejs/kit/pull/15294)) + + Deploying and previewing with Netlify CLI now requires [v17.31.0](https://github.com/netlify/cli/releases/tag/v17.31.0) or later. Run `npm i -g netlify-cli@latest` to upgrade. + +- breaking: require SvelteKit 3 ([#15506](https://github.com/sveltejs/kit/pull/15506)) + + +- breaking: edge function build target is now `es2022` ([#15432](https://github.com/sveltejs/kit/pull/15432)) + + +### Patch Changes + +- Updated dependencies [[`fa335bd`](https://github.com/sveltejs/kit/commit/fa335bd8af3a982d68c4d34b813b68887dc7cce8), [`3031d89`](https://github.com/sveltejs/kit/commit/3031d89087d49297e89b6566108a1a1105c38475), [`cb9d416`](https://github.com/sveltejs/kit/commit/cb9d416d05266b6237f2856fb94df5b8a9b04279), [`caf3a18`](https://github.com/sveltejs/kit/commit/caf3a18de6581ab7d454d6fa8ad37ebdbdef95e6), [`4777827`](https://github.com/sveltejs/kit/commit/4777827246727045cf0209399751377564e7951d), [`a2792e2`](https://github.com/sveltejs/kit/commit/a2792e2cdc2dee91c20c7bfd921a104b539abae5), [`ba36148`](https://github.com/sveltejs/kit/commit/ba36148577ed0bc2675034a3d57001d71d328cd1), [`48e8710`](https://github.com/sveltejs/kit/commit/48e8710889b348a039322e05f3d2672327d56184), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`047d6a0`](https://github.com/sveltejs/kit/commit/047d6a0f1c3fd1bc6f90a81177a2834b023a64b3), [`87603d1`](https://github.com/sveltejs/kit/commit/87603d13d009cada171c9b5d5daad5adad4f72de), [`096962c`](https://github.com/sveltejs/kit/commit/096962c47b0b0f63acd0d539d3973797e4867487), [`d545970`](https://github.com/sveltejs/kit/commit/d54597018308068aea5268e6c45b26398cd57607), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`d06affc`](https://github.com/sveltejs/kit/commit/d06affc9246c676fc32b644efc0bbf53b42bc67d), [`8af47eb`](https://github.com/sveltejs/kit/commit/8af47eb06f9a1a883058de059ccf81aab4e85627), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`5c4d130`](https://github.com/sveltejs/kit/commit/5c4d130ea2953464d8b4b462eb5aa4641aa78da1), [`3f11f35`](https://github.com/sveltejs/kit/commit/3f11f35125c5b3625891f0d00d9c7c826e6eaea2), [`caf3a18`](https://github.com/sveltejs/kit/commit/caf3a18de6581ab7d454d6fa8ad37ebdbdef95e6), [`8823037`](https://github.com/sveltejs/kit/commit/8823037b4a341013cc40a9bb688ee23bda3dbb04), [`1d76212`](https://github.com/sveltejs/kit/commit/1d76212740f59ed58839ef569899c653870834fd), [`0dc0548`](https://github.com/sveltejs/kit/commit/0dc0548307a439b6a6f24b14405366ced213f828), [`00d81fa`](https://github.com/sveltejs/kit/commit/00d81fab618435bac95b1bacb5e9734b61ba1af6)]: + - @sveltejs/kit@3.0.0-next.0 + ## 6.0.4 ### Patch Changes diff --git a/packages/adapter-netlify/ambient.d.ts b/packages/adapter-netlify/ambient.d.ts index a776cd3568f3..662524608f6d 100644 --- a/packages/adapter-netlify/ambient.d.ts +++ b/packages/adapter-netlify/ambient.d.ts @@ -1,7 +1,7 @@ declare global { namespace App { export interface Platform { - context: import('@netlify/functions').Context; + context: import('@netlify/types').Context; } } } diff --git a/packages/adapter-netlify/src/edge.js b/packages/adapter-netlify/files/edge.js similarity index 89% rename from packages/adapter-netlify/src/edge.js rename to packages/adapter-netlify/files/edge.js index f0d9944741ca..518593892b06 100644 --- a/packages/adapter-netlify/src/edge.js +++ b/packages/adapter-netlify/files/edge.js @@ -11,7 +11,6 @@ const server = new Server(manifest); let origin; const initialized = server.init({ - // @ts-ignore env: Deno.env.toObject(), read: async (file) => { const url = `${origin}/${file}`; @@ -31,9 +30,11 @@ const initialized = server.init({ export default async function handler(request, context) { if (!origin) { origin = new URL(request.url).origin; - await initialized; } + // always await initialization to prevent race condition with concurrent requests + await initialized; + return server.respond(request, { platform: { context }, getClientAddress() { diff --git a/packages/adapter-netlify/src/serverless.js b/packages/adapter-netlify/files/serverless.js similarity index 78% rename from packages/adapter-netlify/src/serverless.js rename to packages/adapter-netlify/files/serverless.js index 592eb081b7b9..56502dc95c6c 100644 --- a/packages/adapter-netlify/src/serverless.js +++ b/packages/adapter-netlify/files/serverless.js @@ -1,18 +1,17 @@ -import './shims.js'; import { Server } from '0SERVER'; import { createReadableStream } from '@sveltejs/kit/node'; import process from 'node:process'; /** * @param {import('@sveltejs/kit').SSRManifest} manifest - * @returns {(request: Request, context: import('@netlify/functions').Context) => Promise} + * @returns {(request: Request, context: import('@netlify/types').Context) => Promise} */ export function init(manifest) { const server = new Server(manifest); /** @type {Promise | null} */ let init_promise = server.init({ - env: /** @type {Record} */ (process.env), + env: process.env, read: (file) => createReadableStream(`.netlify/server/${file}`) }); diff --git a/packages/adapter-netlify/files/tsconfig.json b/packages/adapter-netlify/files/tsconfig.json new file mode 100644 index 000000000000..247eef0a082d --- /dev/null +++ b/packages/adapter-netlify/files/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "noEmit": true, + "target": "es2022", + "module": "node16", + "moduleResolution": "node16", + "paths": { + "@sveltejs/kit": ["../../kit/types/index"] + }, + "types": ["deno"] + }, + "include": ["edge.js", "../internal.d.ts"] +} diff --git a/packages/adapter-netlify/index.d.ts b/packages/adapter-netlify/index.d.ts index 580b30cb1060..7306c8b3270f 100644 --- a/packages/adapter-netlify/index.d.ts +++ b/packages/adapter-netlify/index.d.ts @@ -1,4 +1,19 @@ import { Adapter } from '@sveltejs/kit'; import './ambient.d.ts'; -export default function plugin(opts?: { split?: boolean; edge?: boolean }): Adapter; +export interface AdapterOptions { + /** + * If `true`, your app will be deployed as a [Netlify Edge Function](https://docs.netlify.com/build/edge-functions/overview/) rather than the standard Node-based function. + * @default false + */ + edge?: boolean; + /** + * If `true`, your app will be split into multiple functions instead of a single one for the entire app. + * + * If `edge` is `true`, this option cannot be used. + * @default false + */ + split?: boolean; +} + +export default function plugin(opts?: AdapterOptions): Adapter; diff --git a/packages/adapter-netlify/index.js b/packages/adapter-netlify/index.js index cd8ff0521324..579fc579a43c 100644 --- a/packages/adapter-netlify/index.js +++ b/packages/adapter-netlify/index.js @@ -1,11 +1,11 @@ -/** @import { BuildOptions } from 'esbuild' */ -import { appendFileSync, existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'; -import { join, resolve, posix } from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { join, posix } from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; import { builtinModules } from 'node:module'; import process from 'node:process'; -import esbuild from 'esbuild'; import toml from '@iarna/toml'; +import { build } from 'rolldown'; +import { matches, get_publish_directory, s } from './utils.js'; /** * @typedef {{ @@ -14,6 +14,9 @@ import toml from '@iarna/toml'; * } & toml.JsonMap} NetlifyConfig */ +const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf-8')); +const adapter_version = pkg.version; + const name = '@sveltejs/adapter-netlify'; const files = fileURLToPath(new URL('./files', import.meta.url).href); @@ -21,13 +24,16 @@ const edge_set_in_env_var = process.env.NETLIFY_SVELTEKIT_USE_EDGE === 'true' || process.env.NETLIFY_SVELTEKIT_USE_EDGE === '1'; +const netlify_framework_config_path = '.netlify/v1/config.json'; +const netlify_framework_serverless_path = '.netlify/v1/functions'; +const netlify_framework_edge_path = '.netlify/v1/edge-functions'; + const FUNCTION_PREFIX = 'sveltekit-'; -/** @type {import('./index.js').default} */ +/** @type {typeof import('./index.js').default} */ export default function ({ split = false, edge = edge_set_in_env_var } = {}) { return { name, - /** @param {import('@sveltejs/kit').Builder} builder */ async adapt(builder) { if (!builder.routes) { throw new Error( @@ -36,15 +42,15 @@ export default function ({ split = false, edge = edge_set_in_env_var } = {}) { ); } - if (existsSync(`${builder.config.kit.files.assets}/_headers`)) { + if (existsSync(`${builder.config.files.assets}/_headers`)) { throw new Error( - `The _headers file should be placed in the project root rather than the ${builder.config.kit.files.assets} directory` + `The _headers file should be placed in the project root rather than the ${builder.config.files.assets} directory` ); } - if (existsSync(`${builder.config.kit.files.assets}/_redirects`)) { + if (existsSync(`${builder.config.files.assets}/_redirects`)) { throw new Error( - `The _redirects file should be placed in the project root rather than the ${builder.config.kit.files.assets} directory` + `The _redirects file should be placed in the project root rather than the ${builder.config.files.assets} directory` ); } @@ -54,16 +60,19 @@ export default function ({ split = false, edge = edge_set_in_env_var } = {}) { const publish = get_publish_directory(netlify_config, builder) || 'build'; // empty out existing build directories - builder.rimraf(publish); - builder.rimraf('.netlify/edge-functions'); - builder.rimraf('.netlify/server'); - builder.rimraf('.netlify/package.json'); - builder.rimraf('.netlify/serverless.js'); - + rmSync(publish, { force: true, recursive: true }); + rmSync('.netlify/v1', { force: true, recursive: true }); + + // clean up legacy directories from older adapter versions to avoid + // gnarly edge cases when an existing project is upgraded to this version + rmSync('.netlify/edge-functions', { force: true, recursive: true }); + rmSync('.netlify/server', { force: true, recursive: true }); + rmSync('.netlify/package.json', { force: true, recursive: true }); + rmSync('.netlify/serverless.js', { force: true, recursive: true }); if (existsSync('.netlify/functions-internal')) { for (const file of readdirSync('.netlify/functions-internal')) { if (file.startsWith(FUNCTION_PREFIX)) { - builder.rimraf(join('.netlify/functions-internal', file)); + rmSync(join('.netlify/functions-internal', file), { force: true, recursive: true }); } } } @@ -71,17 +80,17 @@ export default function ({ split = false, edge = edge_set_in_env_var } = {}) { builder.log.minor(`Publishing to "${publish}"`); builder.log.minor('Copying assets...'); - const publish_dir = `${publish}${builder.config.kit.paths.base}`; + const publish_dir = `${publish}${builder.config.paths.base}`; builder.writeClient(publish_dir); builder.writePrerendered(publish_dir); - builder.log.minor('Writing custom headers...'); - const headers_file = join(publish, '_headers'); - builder.copy('_headers', headers_file); - appendFileSync( - headers_file, - `\n\n/${builder.getAppPath()}/immutable/*\n cache-control: public\n cache-control: immutable\n cache-control: max-age=31536000\n` - ); + // Copy user's custom _headers file if it exists + if (existsSync('_headers')) { + builder.copy('_headers', join(publish, '_headers')); + } + + builder.log.minor('Writing Netlify config...'); + write_frameworks_config({ builder }); if (edge) { if (split) { @@ -90,7 +99,7 @@ export default function ({ split = false, edge = edge_set_in_env_var } = {}) { await generate_edge_functions({ builder }); } else { - generate_lambda_functions({ builder, split, publish }); + generate_serverless_functions({ builder, split, publish }); } }, @@ -100,131 +109,24 @@ export default function ({ split = false, edge = edge_set_in_env_var } = {}) { } }; } -/** - * @param { object } params - * @param {import('@sveltejs/kit').Builder} params.builder - */ -async function generate_edge_functions({ builder }) { - const tmp = builder.getBuildDirectory('netlify-tmp'); - builder.rimraf(tmp); - builder.mkdirp(tmp); - - builder.mkdirp('.netlify/edge-functions'); - - builder.log.minor('Generating Edge Function...'); - const relativePath = posix.relative(tmp, builder.getServerDirectory()); - - builder.copy(`${files}/edge.js`, `${tmp}/entry.js`, { - replace: { - '0SERVER': `${relativePath}/index.js`, - MANIFEST: './manifest.js' - } - }); - - const manifest = builder.generateManifest({ - relativePath - }); - - writeFileSync(`${tmp}/manifest.js`, `export const manifest = ${manifest};\n`); - - /** @type {{ assets: Set }} */ - // we have to prepend the file:// protocol because Windows doesn't support absolute path imports - const { assets } = (await import(`file://${tmp}/manifest.js`)).manifest; - const path = '/*'; - // We only need to specify paths without the trailing slash because - // Netlify will handle the optional trailing slash for us - const excluded = [ - // Contains static files - `/${builder.getAppPath()}/immutable/*`, - `/${builder.getAppPath()}/version.json`, - ...builder.prerendered.paths, - ...Array.from(assets).flatMap((asset) => { - if (asset.endsWith('/index.html')) { - const dir = asset.replace(/\/index\.html$/, ''); - return [ - `${builder.config.kit.paths.base}/${asset}`, - `${builder.config.kit.paths.base}/${dir}` - ]; - } - return `${builder.config.kit.paths.base}/${asset}`; - }), - // Should not be served by SvelteKit at all - '/.netlify/*' - ]; - - /** @type {import('@netlify/edge-functions').Manifest} */ - const edge_manifest = { - functions: [ - { - function: 'render', - path, - excludedPath: /** @type {`/${string}`[]} */ (excluded) - } - ], - version: 1 - }; - - /** @type {BuildOptions} */ - const esbuild_config = { - bundle: true, - format: 'esm', - platform: 'browser', - sourcemap: 'linked', - target: 'es2020', - loader: { - '.wasm': 'copy', - '.woff': 'copy', - '.woff2': 'copy', - '.ttf': 'copy', - '.eot': 'copy', - '.otf': 'copy' - }, - // Node built-ins are allowed, but must be prefixed with `node:` - // https://docs.netlify.com/edge-functions/api/#runtime-environment - external: builtinModules.map((id) => `node:${id}`), - alias: Object.fromEntries(builtinModules.map((id) => [id, `node:${id}`])) - }; - await Promise.all([ - esbuild.build({ - entryPoints: [`${tmp}/entry.js`], - outfile: '.netlify/edge-functions/render.js', - ...esbuild_config - }), - builder.hasServerInstrumentationFile() && - esbuild.build({ - entryPoints: [`${builder.getServerDirectory()}/instrumentation.server.js`], - outfile: '.netlify/edge/instrumentation.server.js', - ...esbuild_config - }) - ]); - - if (builder.hasServerInstrumentationFile()) { - builder.instrument({ - entrypoint: '.netlify/edge-functions/render.js', - instrumentation: '.netlify/edge/instrumentation.server.js', - start: '.netlify/edge/start.js' - }); - } - - writeFileSync('.netlify/edge-functions/manifest.json', JSON.stringify(edge_manifest)); -} /** * @param { object } params * @param {import('@sveltejs/kit').Builder} params.builder * @param { string } params.publish * @param { boolean } params.split */ -function generate_lambda_functions({ builder, publish, split }) { - builder.mkdirp('.netlify/functions-internal/.svelte-kit'); +function generate_serverless_functions({ builder, publish, split }) { + // https://docs.netlify.com/build/frameworks/frameworks-api/#netlifyv1functions + mkdirSync(netlify_framework_serverless_path, { recursive: true }); - builder.writeServer('.netlify/server'); + builder.writeServer('.netlify/v1/server'); const replace = { '0SERVER': './server/index.js' // digit prefix prevents CJS build from using this as a variable name, which would also get replaced }; - builder.copy(files, '.netlify', { replace, filter: (name) => !name.endsWith('edge.js') }); + builder.copy(files, '.netlify/v1', { replace, filter: (file) => !file.endsWith('edge.js') }); builder.log.minor('Generating serverless functions...'); @@ -306,77 +208,43 @@ function generate_lambda_functions({ builder, publish, split }) { } } +/** + * @returns {NetlifyConfig | null} + */ function get_netlify_config() { if (!existsSync('netlify.toml')) return null; try { - return /** @type {NetlifyConfig} */ (toml.parse(readFileSync('netlify.toml', 'utf-8'))); + return toml.parse(readFileSync('netlify.toml', 'utf-8')); } catch (err) { if (err instanceof Error) { - err.message = `Error parsing netlify.toml: ${err.message}`; + throw new Error(`Failed to parse netlify.toml: ${err.message}`, { cause: err }); } throw err; } } /** - * @param {NetlifyConfig | null} netlify_config - * @param {import('@sveltejs/kit').Builder} builder - **/ -function get_publish_directory(netlify_config, builder) { - if (netlify_config) { - if (!netlify_config.build?.publish) { - builder.log.minor('No publish directory specified in netlify.toml, using default'); - return; - } - - if (resolve(netlify_config.build.publish) === process.cwd()) { - throw new Error( - 'The publish directory cannot be set to the site root. Please change it to another value such as "build" in netlify.toml.' - ); - } - return netlify_config.build.publish; - } - - builder.log.warn( - 'No netlify.toml found. Using default publish directory. Consult https://svelte.dev/docs/kit/adapter-netlify#usage for more details' - ); -} - -/** - * @typedef {{ rest: boolean, dynamic: boolean, content: string }} RouteSegment + * Writes the Netlify Frameworks API config file + * https://docs.netlify.com/build/frameworks/frameworks-api/ + * @param {{ builder: import('@sveltejs/kit').Builder }} params */ - -/** - * @param {RouteSegment[]} a - * @param {RouteSegment[]} b - * @returns {boolean} - */ -function matches(a, b) { - if (a[0] && b[0]) { - if (b[0].rest) { - if (b.length === 1) return true; - - const next_b = b.slice(1); - - for (let i = 0; i < a.length; i += 1) { - if (matches(a.slice(i), next_b)) return true; +function write_frameworks_config({ builder }) { + // https://docs.netlify.com/build/frameworks/frameworks-api/#headers + /** @type {{ headers: Array<{ for: string, values: Record }> }} */ + const config = { + headers: [ + { + for: `/${builder.getAppPath()}/immutable/*`, + values: { + 'cache-control': 'public, immutable, max-age=31536000' + } } + ] + }; - return false; - } - - if (!b[0].dynamic) { - if (!a[0].dynamic && a[0].content !== b[0].content) return false; - } - - if (a.length === 1 && b.length === 1) return true; - return matches(a.slice(1), b.slice(1)); - } else if (a[0]) { - return a.length === 1 && a[0].rest; - } else { - return b.length === 1 && b[0].rest; - } + mkdirSync('.netlify/v1', { recursive: true }); + writeFileSync(netlify_framework_config_path, s(config)); } /** @@ -396,20 +264,20 @@ function generate_serverless_function({ builder, routes, patterns, name, exclude }); const fn = generate_serverless_function_module(manifest); - const config = generate_config_export(patterns, exclude); + const config = generate_config_export(name, patterns, exclude); if (builder.hasServerInstrumentationFile()) { - writeFileSync(`.netlify/functions-internal/${name}.mjs`, fn); + writeFileSync(`${netlify_framework_serverless_path}/${name}.mjs`, fn); builder.instrument({ - entrypoint: `.netlify/functions-internal/${name}.mjs`, - instrumentation: '.netlify/server/instrumentation.server.js', - start: `.netlify/functions-start/${name}.start.mjs`, + entrypoint: `${netlify_framework_serverless_path}/${name}.mjs`, + instrumentation: '.netlify/v1/server/instrumentation.server.js', + start: `.netlify/v1/server/${name}.start.mjs`, module: { generateText: generate_traced_module(config) } }); } else { - writeFileSync(`.netlify/functions-internal/${name}.mjs`, `${fn}\n${config}`); + writeFileSync(`${netlify_framework_serverless_path}/${name}.mjs`, `${fn}\n${config}`); } } @@ -425,17 +293,24 @@ export default init(${manifest}); `; } +const generator_string = `@sveltejs/adapter-netlify@${adapter_version}`; + /** + * @param {string} name The name that shows up in the logs & metrics functions list * @param {string[]} patterns * @param {string[]} [exclude] * @returns {string} */ -function generate_config_export(patterns, exclude = []) { +function generate_config_export(name, patterns, exclude = []) { // TODO: add a human friendly name for the function https://docs.netlify.com/build/frameworks/frameworks-api/#configuration-options-2 + + // https://docs.netlify.com/build/frameworks/frameworks-api/#configuration-options-2 return `\ export const config = { - path: [${patterns.map((s) => JSON.stringify(s)).join(', ')}], - excludedPath: [${['/.netlify/*', ...exclude].map((s) => JSON.stringify(s)).join(', ')}], + name: ${JSON.stringify(name)}, + generator: '${generator_string}', + path: [${patterns.map(s).join(', ')}], + excludedPath: [${['/.netlify/*', ...exclude].map(s).join(', ')}], preferStatic: true }; `; @@ -448,10 +323,132 @@ export const config = { function generate_traced_module(config) { return ({ instrumentation, start }) => { return `\ -import './${instrumentation}'; -const { default: _0 } = await import('./${start}'); +import '../server/${instrumentation}'; +const { default: _0 } = await import('../server/${start}'); export { _0 as default }; ${config}`; }; } + +/** @satisfies {import('rolldown').BuildOptions} */ +const rolldown_config = { + platform: 'browser', + output: { + sourcemap: true, + codeSplitting: false + }, + transform: { + target: 'es2022' + }, + // Node built-ins are allowed, but must be prefixed with `node:` + // https://docs.netlify.com/edge-functions/api/#runtime-environment + external: builtinModules.map((id) => `node:${id}`), + resolve: { + alias: Object.fromEntries(builtinModules.map((id) => [id, `node:${id}`])) + } +}; + +/** + * @param { object } params + * @param {import('@sveltejs/kit').Builder} params.builder + */ +async function generate_edge_functions({ builder }) { + const tmp = builder.getBuildDirectory('netlify-tmp'); + rmSync(tmp, { force: true, recursive: true }); + mkdirSync(tmp, { recursive: true }); + + // https://docs.netlify.com/build/frameworks/frameworks-api/#edge-functions + mkdirSync('.netlify/v1/edge-functions', { recursive: true }); + + builder.log.minor('Generating Edge Function...'); + const relativePath = posix.relative(tmp, builder.getServerDirectory()); + + builder.copy(`${files}/edge.js`, `${tmp}/entry.js`, { + replace: { + '0SERVER': `${relativePath}/index.js`, + MANIFEST: './manifest.js' + } + }); + + const manifest = builder.generateManifest({ + relativePath + }); + + writeFileSync(`${tmp}/manifest.js`, `export const manifest = ${manifest};\n`); + + /** @type {{ assets: Set }} */ + const { assets } = (await import(pathToFileURL(`${tmp}/manifest.js`).href)).manifest; + + const path = '/*'; + // We only need to specify paths without the trailing slash because + // Netlify will handle the optional trailing slash for us + const excluded_paths = [ + // Contains static files + `/${builder.getAppPath()}/immutable/*`, + `/${builder.getAppPath()}/version.json`, + // the base root and `trailingSlash: 'always'` pages are recorded with a trailing slash + ...builder.prerendered.paths.map((path) => (path === '/' ? path : path.replace(/\/$/, ''))), + ...Array.from(assets).flatMap((asset) => { + if (asset.endsWith('/index.html')) { + const dir = asset.replace(/\/index\.html$/, ''); + return [`${builder.config.paths.base}/${asset}`, `${builder.config.paths.base}/${dir}`]; + } + return `${builder.config.paths.base}/${asset}`; + }), + // Should not be served by SvelteKit at all + '/.netlify/*' + ]; + + await Promise.all([ + build({ + ...rolldown_config, + input: `${tmp}/entry.js`, + output: { + ...rolldown_config.output, + file: `${netlify_framework_edge_path}/${FUNCTION_PREFIX}render.js` + } + }), + builder.hasServerInstrumentationFile() && + build({ + ...rolldown_config, + input: `${builder.getServerDirectory()}/instrumentation.server.js`, + output: { + ...rolldown_config.output, + file: `${netlify_framework_edge_path}/${FUNCTION_PREFIX}instrumentation.server.js` + } + }) + ]); + + if (builder.hasServerInstrumentationFile()) { + builder.instrument({ + entrypoint: `${netlify_framework_edge_path}/${FUNCTION_PREFIX}render.js`, + instrumentation: `${netlify_framework_edge_path}/${FUNCTION_PREFIX}instrumentation.server.js`, + start: `${netlify_framework_edge_path}/${FUNCTION_PREFIX}start.js` + }); + } + + add_edge_function_config({ builder, path, excluded_paths }); +} + +/** + * Adds edge function configuration to the Frameworks API config file `config.json` + * https://docs.netlify.com/build/frameworks/frameworks-api/#netlifyv1edge-functions + * @param {{ builder: import('@sveltejs/kit').Builder, path: string, excluded_paths: string[] }} params + */ +function add_edge_function_config({ path, excluded_paths }) { + const config = JSON.parse(readFileSync(netlify_framework_config_path, 'utf-8')); + + // https://docs.netlify.com/build/frameworks/frameworks-api/#configuration-options-1 + config.edge_functions = [ + { + function: `${FUNCTION_PREFIX}render`, + name: 'SvelteKit server', + generator: generator_string, + path, + excludedPath: excluded_paths + } + ]; + + writeFileSync(netlify_framework_config_path, s(config)); +} diff --git a/packages/adapter-netlify/package.json b/packages/adapter-netlify/package.json index fd130d16aee7..72e3c75473b2 100644 --- a/packages/adapter-netlify/package.json +++ b/packages/adapter-netlify/package.json @@ -1,6 +1,6 @@ { "name": "@sveltejs/adapter-netlify", - "version": "6.0.4", + "version": "7.0.0-next.8", "description": "A SvelteKit adapter that creates a Netlify app", "keywords": [ "adapter", @@ -27,36 +27,31 @@ }, "types": "index.d.ts", "files": [ - "files", + "files/*.js", "index.js", + "utils.js", "index.d.ts", "ambient.d.ts" ], "scripts": { - "dev": "rollup -cw", - "build": "rollup -c", - "check": "tsc", - "lint": "prettier --check .", - "format": "pnpm lint --write", + "check": "tsc && tsc --project files/tsconfig.json", + "lint": "oxfmt --check .", + "format": "oxfmt --write .", "test:unit": "vitest run", - "test:build": "pnpm build && pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" build", - "test": "pnpm test:build", - "prepublishOnly": "pnpm build" + "test:build": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" build", + "test": "pnpm test:unit && pnpm test:build" }, "dependencies": { "@iarna/toml": "^2.2.5", - "esbuild": "^0.28.1" + "@netlify/types": "^2.8.0", + "rolldown": "^1.2.3" }, "devDependencies": { - "@netlify/edge-functions": "catalog:", - "@netlify/functions": "catalog:", - "@rollup/plugin-commonjs": "catalog:", - "@rollup/plugin-json": "catalog:", - "@rollup/plugin-node-resolve": "catalog:", + "@netlify/edge-functions": "^3.0.8", "@sveltejs/kit": "workspace:^", + "@types/deno": "^2.7.0", "@types/node": "catalog:", - "rollup": "^4.59.0", - "typescript": "catalog:", + "typescript": "catalog:typescript-native", "vitest": "catalog:" }, "peerDependencies": { diff --git a/packages/adapter-netlify/rollup.config.js b/packages/adapter-netlify/rollup.config.js deleted file mode 100644 index 82d61b85d29d..000000000000 --- a/packages/adapter-netlify/rollup.config.js +++ /dev/null @@ -1,40 +0,0 @@ -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import json from '@rollup/plugin-json'; -import { rmSync } from 'node:fs'; - -/** - * @param {string} filepath - * @returns {import('rollup').Plugin} - */ -function clearOutput(filepath) { - return { - name: 'clear-output', - buildStart: { - order: 'pre', - sequential: true, - handler() { - rmSync(filepath, { recursive: true, force: true }); - } - } - }; -} - -/** @type {import('rollup').RollupOptions} */ -const config = { - input: { - serverless: 'src/serverless.js', - shims: 'src/shims.js', - edge: 'src/edge.js' - }, - output: { - dir: 'files', - format: 'esm' - }, - // @ts-ignore https://github.com/rollup/plugins/issues/1329 - plugins: [clearOutput('files'), nodeResolve({ preferBuiltins: true }), commonjs(), json()], - external: (id) => id === '0SERVER' || id === 'MANIFEST' || id.startsWith('node:'), - preserveEntrySignatures: 'exports-only' -}; - -export default config; diff --git a/packages/adapter-netlify/src/shims.js b/packages/adapter-netlify/src/shims.js deleted file mode 100644 index 2490311daa1e..000000000000 --- a/packages/adapter-netlify/src/shims.js +++ /dev/null @@ -1,2 +0,0 @@ -import { installPolyfills } from '@sveltejs/kit/node/polyfills'; -installPolyfills(); diff --git a/packages/adapter-netlify/test/apps/basic/.gitignore b/packages/adapter-netlify/test/apps/basic/.gitignore index 88f661c765c6..4150a3674e3a 100644 --- a/packages/adapter-netlify/test/apps/basic/.gitignore +++ b/packages/adapter-netlify/test/apps/basic/.gitignore @@ -3,4 +3,4 @@ node_modules /.svelte-kit /.netlify /build -deno.lock \ No newline at end of file +deno.lock diff --git a/packages/adapter-netlify/test/apps/basic/_headers b/packages/adapter-netlify/test/apps/basic/_headers new file mode 100644 index 000000000000..01c0aab8e046 --- /dev/null +++ b/packages/adapter-netlify/test/apps/basic/_headers @@ -0,0 +1,2 @@ +/custom-header-path + X-Custom-Header: test-value diff --git a/packages/adapter-netlify/test/apps/basic/_redirects b/packages/adapter-netlify/test/apps/basic/_redirects new file mode 100644 index 000000000000..fd5703a26e6a --- /dev/null +++ b/packages/adapter-netlify/test/apps/basic/_redirects @@ -0,0 +1 @@ +/redirect-me /greeting/redirected 301 diff --git a/packages/adapter-netlify/test/apps/basic/package.json b/packages/adapter-netlify/test/apps/basic/package.json index 16540a0375c0..efbe559d533f 100644 --- a/packages/adapter-netlify/test/apps/basic/package.json +++ b/packages/adapter-netlify/test/apps/basic/package.json @@ -6,7 +6,6 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "prepare": "svelte-kit sync || echo ''", "test:platform": "playwright test" }, "devDependencies": { diff --git a/packages/adapter-netlify/test/apps/basic/src/instrumentation.server.js b/packages/adapter-netlify/test/apps/basic/src/instrumentation.server.js deleted file mode 100644 index acc9022e1d64..000000000000 --- a/packages/adapter-netlify/test/apps/basic/src/instrumentation.server.js +++ /dev/null @@ -1 +0,0 @@ -// this is just here to make sure the changes resulting from it work diff --git a/packages/adapter-netlify/test/apps/basic/src/routes/+page.svelte b/packages/adapter-netlify/test/apps/basic/src/routes/+page.svelte new file mode 100644 index 000000000000..03c350d48255 --- /dev/null +++ b/packages/adapter-netlify/test/apps/basic/src/routes/+page.svelte @@ -0,0 +1 @@ +

Hello from SvelteKit

diff --git a/packages/adapter-netlify/test/apps/basic/src/routes/greeting/[name]/+page.server.js b/packages/adapter-netlify/test/apps/basic/src/routes/greeting/[name]/+page.server.js new file mode 100644 index 000000000000..31e005a89bad --- /dev/null +++ b/packages/adapter-netlify/test/apps/basic/src/routes/greeting/[name]/+page.server.js @@ -0,0 +1,4 @@ +/** @type {import('./$types').PageServerLoad} */ +export function load({ params }) { + return { name: params.name }; +} diff --git a/packages/adapter-netlify/test/apps/basic/src/routes/greeting/[name]/+page.svelte b/packages/adapter-netlify/test/apps/basic/src/routes/greeting/[name]/+page.svelte new file mode 100644 index 000000000000..4dcbba568369 --- /dev/null +++ b/packages/adapter-netlify/test/apps/basic/src/routes/greeting/[name]/+page.svelte @@ -0,0 +1,5 @@ + + +

Hello {data.name}

diff --git a/packages/adapter-netlify/test/apps/basic/svelte.config.js b/packages/adapter-netlify/test/apps/basic/svelte.config.js deleted file mode 100644 index 050579db13ba..000000000000 --- a/packages/adapter-netlify/test/apps/basic/svelte.config.js +++ /dev/null @@ -1,15 +0,0 @@ -import adapter from '../../../index.js'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter(), - experimental: { - instrumentation: { - server: true - } - } - } -}; - -export default config; diff --git a/packages/adapter-netlify/test/apps/basic/test/test.js b/packages/adapter-netlify/test/apps/basic/test/test.js index 50329932eb04..b208dd54a8b7 100644 --- a/packages/adapter-netlify/test/apps/basic/test/test.js +++ b/packages/adapter-netlify/test/apps/basic/test/test.js @@ -1,12 +1,37 @@ import fs from 'node:fs'; import path from 'node:path'; -import { fileURLToPath } from 'node:url'; import { expect, test } from '@playwright/test'; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); +test('page renders', async ({ request }) => { + const response = await request.get('/'); + expect(response.status()).toBe(200); + expect(await response.text()).toContain('Hello from SvelteKit'); +}); + +test('dynamic route works', async ({ request }) => { + const response = await request.get('/greeting/world'); + expect(response.status()).toBe(200); + expect(await response.text()).toContain('Hello world'); +}); test('read from $app/server works', async ({ request }) => { - const content = fs.readFileSync(path.resolve(__dirname, '../src/routes/read/file.txt'), 'utf-8'); + const content = fs.readFileSync( + path.resolve(import.meta.dirname, '../src/routes/read/file.txt'), + 'utf-8' + ); const response = await request.get('/read'); expect(await response.text()).toBe(content); }); + +test('_redirects are copied to publish directory', () => { + const redirects = fs.readFileSync( + path.resolve(import.meta.dirname, '../build/_redirects'), + 'utf-8' + ); + expect(redirects).toContain('/redirect-me /greeting/redirected 301'); +}); + +test('_headers are copied to publish directory', () => { + const headers = fs.readFileSync(path.resolve(import.meta.dirname, '../build/_headers'), 'utf-8'); + expect(headers).toContain('X-Custom-Header: test-value'); +}); diff --git a/packages/adapter-netlify/test/apps/basic/tsconfig.json b/packages/adapter-netlify/test/apps/basic/tsconfig.json index c2db702f9bb1..0c2092500732 100644 --- a/packages/adapter-netlify/test/apps/basic/tsconfig.json +++ b/packages/adapter-netlify/test/apps/basic/tsconfig.json @@ -9,5 +9,5 @@ "sourceMap": true, "moduleResolution": "bundler" }, - "extends": "./.svelte-kit/tsconfig.json" + "extends": "$app/tsconfig" } diff --git a/packages/adapter-netlify/test/apps/basic/vite.config.ts b/packages/adapter-netlify/test/apps/basic/vite.config.ts index 72a307cc0e56..8c3bc9c5e4a7 100644 --- a/packages/adapter-netlify/test/apps/basic/vite.config.ts +++ b/packages/adapter-netlify/test/apps/basic/vite.config.ts @@ -1,11 +1,16 @@ import { sveltekit } from '@sveltejs/kit/vite'; import type { UserConfig } from 'vite'; +import adapter from '../../../index.js'; const config: UserConfig = { build: { minify: false }, - plugins: [sveltekit()] + plugins: [ + sveltekit({ + adapter: adapter() + }) + ] }; export default config; diff --git a/packages/adapter-netlify/test/apps/edge/_headers b/packages/adapter-netlify/test/apps/edge/_headers new file mode 100644 index 000000000000..01c0aab8e046 --- /dev/null +++ b/packages/adapter-netlify/test/apps/edge/_headers @@ -0,0 +1,2 @@ +/custom-header-path + X-Custom-Header: test-value diff --git a/packages/adapter-netlify/test/apps/edge/_redirects b/packages/adapter-netlify/test/apps/edge/_redirects new file mode 100644 index 000000000000..fd5703a26e6a --- /dev/null +++ b/packages/adapter-netlify/test/apps/edge/_redirects @@ -0,0 +1 @@ +/redirect-me /greeting/redirected 301 diff --git a/packages/adapter-netlify/test/apps/edge/package.json b/packages/adapter-netlify/test/apps/edge/package.json index a8bed2c4f7bb..873608fb8298 100644 --- a/packages/adapter-netlify/test/apps/edge/package.json +++ b/packages/adapter-netlify/test/apps/edge/package.json @@ -6,7 +6,6 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "prepare": "svelte-kit sync || echo ''", "test:platform": "playwright test" }, "devDependencies": { diff --git a/packages/adapter-netlify/test/apps/edge/src/routes/+page.svelte b/packages/adapter-netlify/test/apps/edge/src/routes/+page.svelte new file mode 100644 index 000000000000..03c350d48255 --- /dev/null +++ b/packages/adapter-netlify/test/apps/edge/src/routes/+page.svelte @@ -0,0 +1 @@ +

Hello from SvelteKit

diff --git a/packages/adapter-netlify/test/apps/edge/src/routes/greeting/[name]/+page.server.js b/packages/adapter-netlify/test/apps/edge/src/routes/greeting/[name]/+page.server.js new file mode 100644 index 000000000000..31e005a89bad --- /dev/null +++ b/packages/adapter-netlify/test/apps/edge/src/routes/greeting/[name]/+page.server.js @@ -0,0 +1,4 @@ +/** @type {import('./$types').PageServerLoad} */ +export function load({ params }) { + return { name: params.name }; +} diff --git a/packages/adapter-netlify/test/apps/edge/src/routes/greeting/[name]/+page.svelte b/packages/adapter-netlify/test/apps/edge/src/routes/greeting/[name]/+page.svelte new file mode 100644 index 000000000000..4dcbba568369 --- /dev/null +++ b/packages/adapter-netlify/test/apps/edge/src/routes/greeting/[name]/+page.svelte @@ -0,0 +1,5 @@ + + +

Hello {data.name}

diff --git a/packages/adapter-netlify/test/apps/edge/svelte.config.js b/packages/adapter-netlify/test/apps/edge/svelte.config.js deleted file mode 100644 index c633196b8ea8..000000000000 --- a/packages/adapter-netlify/test/apps/edge/svelte.config.js +++ /dev/null @@ -1,12 +0,0 @@ -import adapter from '../../../index.js'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter({ - edge: true - }) - } -}; - -export default config; diff --git a/packages/adapter-netlify/test/apps/edge/test/test.js b/packages/adapter-netlify/test/apps/edge/test/test.js index aca60b059dee..8460b09a745d 100644 --- a/packages/adapter-netlify/test/apps/edge/test/test.js +++ b/packages/adapter-netlify/test/apps/edge/test/test.js @@ -1,22 +1,38 @@ import fs from 'node:fs'; import path from 'node:path'; -import { fileURLToPath } from 'node:url'; import { expect, test } from '@playwright/test'; -// TODO: use import.meta.dirname in Kit 3 -const __dirname = path.dirname(fileURLToPath(import.meta.url)); +test('page renders', async ({ request }) => { + const response = await request.get('/'); + expect(response.status()).toBe(200); + expect(await response.text()).toContain('Hello from SvelteKit'); +}); + +test('dynamic route works', async ({ request }) => { + const response = await request.get('/greeting/world'); + expect(response.status()).toBe(200); + expect(await response.text()).toContain('Hello world'); +}); test('read from $app/server works', async ({ request }) => { - const content = fs.readFileSync(path.resolve(__dirname, '../src/routes/read/file.txt'), 'utf-8'); + const content = fs.readFileSync( + path.resolve(import.meta.dirname, '../src/routes/read/file.txt'), + 'utf-8' + ); const response = await request.get('/read'); expect(await response.text()).toBe(content); }); +test('_headers are copied to publish directory', () => { + const headers = fs.readFileSync(path.resolve(import.meta.dirname, '../build/_headers'), 'utf-8'); + expect(headers).toContain('X-Custom-Header: test-value'); +}); + test('treeshakes component from the server bundle if SSR is turned off', async ({ page }) => { await page.goto('/treeshake-server'); const component_text = 'this should never appear in the server bundle'; const server_bundle = fs.readFileSync( - path.resolve(__dirname, '../.netlify/edge-functions/render.js'), + path.resolve(import.meta.dirname, '../.netlify/v1/edge-functions/sveltekit-render.js'), 'utf-8' ); expect(server_bundle).not.toContain(component_text); diff --git a/packages/adapter-netlify/test/apps/edge/tsconfig.json b/packages/adapter-netlify/test/apps/edge/tsconfig.json index c2db702f9bb1..0c2092500732 100644 --- a/packages/adapter-netlify/test/apps/edge/tsconfig.json +++ b/packages/adapter-netlify/test/apps/edge/tsconfig.json @@ -9,5 +9,5 @@ "sourceMap": true, "moduleResolution": "bundler" }, - "extends": "./.svelte-kit/tsconfig.json" + "extends": "$app/tsconfig" } diff --git a/packages/adapter-netlify/test/apps/edge/vite.config.ts b/packages/adapter-netlify/test/apps/edge/vite.config.ts index 72a307cc0e56..8d97a74850b9 100644 --- a/packages/adapter-netlify/test/apps/edge/vite.config.ts +++ b/packages/adapter-netlify/test/apps/edge/vite.config.ts @@ -1,11 +1,18 @@ import { sveltekit } from '@sveltejs/kit/vite'; import type { UserConfig } from 'vite'; +import adapter from '../../../index.js'; const config: UserConfig = { build: { minify: false }, - plugins: [sveltekit()] + plugins: [ + sveltekit({ + adapter: adapter({ + edge: true + }) + }) + ] }; export default config; diff --git a/packages/adapter-netlify/test/apps/instrumentation/.gitignore b/packages/adapter-netlify/test/apps/instrumentation/.gitignore new file mode 100644 index 000000000000..88f661c765c6 --- /dev/null +++ b/packages/adapter-netlify/test/apps/instrumentation/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +node_modules +/.svelte-kit +/.netlify +/build +deno.lock \ No newline at end of file diff --git a/packages/adapter-netlify/test/apps/instrumentation/netlify.toml b/packages/adapter-netlify/test/apps/instrumentation/netlify.toml new file mode 100644 index 000000000000..0183026741cd --- /dev/null +++ b/packages/adapter-netlify/test/apps/instrumentation/netlify.toml @@ -0,0 +1,2 @@ +[build] +publish = "build" diff --git a/packages/kit/test/build-errors/apps/service-worker-dynamic-public-env/package.json b/packages/adapter-netlify/test/apps/instrumentation/package.json similarity index 57% rename from packages/kit/test/build-errors/apps/service-worker-dynamic-public-env/package.json rename to packages/adapter-netlify/test/apps/instrumentation/package.json index 08a2e94abd8d..f234bc41a823 100644 --- a/packages/kit/test/build-errors/apps/service-worker-dynamic-public-env/package.json +++ b/packages/adapter-netlify/test/apps/instrumentation/package.json @@ -1,21 +1,17 @@ { - "name": "service-worker-dynamic-public-env", + "name": "test-netlify-instrumentation", "version": "0.0.1", "private": true, "scripts": { "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "prepare": "svelte-kit sync", - "check": "svelte-kit sync && tsc && svelte-check", - "check:watch": "svelte-check --watch" + "test:platform": "playwright test" }, "devDependencies": { "@sveltejs/kit": "workspace:^", "@sveltejs/vite-plugin-svelte": "catalog:", "svelte": "catalog:", - "svelte-check": "catalog:", - "typescript": "catalog:", "vite": "catalog:" }, "type": "module" diff --git a/packages/adapter-netlify/test/apps/instrumentation/playwright.config.js b/packages/adapter-netlify/test/apps/instrumentation/playwright.config.js new file mode 100644 index 000000000000..33d36b651014 --- /dev/null +++ b/packages/adapter-netlify/test/apps/instrumentation/playwright.config.js @@ -0,0 +1 @@ +export { config as default } from '../../utils.js'; diff --git a/packages/adapter-netlify/test/apps/instrumentation/src/app.html b/packages/adapter-netlify/test/apps/instrumentation/src/app.html new file mode 100644 index 000000000000..26bc25de4e52 --- /dev/null +++ b/packages/adapter-netlify/test/apps/instrumentation/src/app.html @@ -0,0 +1,12 @@ + + + + + + %sveltekit.head% + + + +
%sveltekit.body%
+ + diff --git a/packages/adapter-netlify/test/apps/instrumentation/src/instrumentation.server.js b/packages/adapter-netlify/test/apps/instrumentation/src/instrumentation.server.js new file mode 100644 index 000000000000..00368839194c --- /dev/null +++ b/packages/adapter-netlify/test/apps/instrumentation/src/instrumentation.server.js @@ -0,0 +1 @@ +globalThis.__INSTRUMENTATION_RAN__ = true; diff --git a/packages/adapter-netlify/test/apps/instrumentation/src/routes/+page.svelte b/packages/adapter-netlify/test/apps/instrumentation/src/routes/+page.svelte new file mode 100644 index 000000000000..03c350d48255 --- /dev/null +++ b/packages/adapter-netlify/test/apps/instrumentation/src/routes/+page.svelte @@ -0,0 +1 @@ +

Hello from SvelteKit

diff --git a/packages/adapter-netlify/test/apps/instrumentation/src/routes/instrumented/+server.js b/packages/adapter-netlify/test/apps/instrumentation/src/routes/instrumented/+server.js new file mode 100644 index 000000000000..def88b135b5f --- /dev/null +++ b/packages/adapter-netlify/test/apps/instrumentation/src/routes/instrumented/+server.js @@ -0,0 +1,3 @@ +export function GET() { + return new Response(String(globalThis.__INSTRUMENTATION_RAN__ === true)); +} diff --git a/packages/adapter-netlify/test/apps/instrumentation/test/test.js b/packages/adapter-netlify/test/apps/instrumentation/test/test.js new file mode 100644 index 000000000000..452b099714a3 --- /dev/null +++ b/packages/adapter-netlify/test/apps/instrumentation/test/test.js @@ -0,0 +1,13 @@ +import { expect, test } from '@playwright/test'; + +test('page renders', async ({ request }) => { + const response = await request.get('/'); + expect(response.status()).toBe(200); + expect(await response.text()).toContain('Hello from SvelteKit'); +}); + +test('instrumentation.server.js runs at startup', async ({ request }) => { + const response = await request.get('/instrumented'); + expect(response.status()).toBe(200); + expect(await response.text()).toBe('true'); +}); diff --git a/packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/tsconfig.json b/packages/adapter-netlify/test/apps/instrumentation/tsconfig.json similarity index 63% rename from packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/tsconfig.json rename to packages/adapter-netlify/test/apps/instrumentation/tsconfig.json index 8e767983eeb2..12550521f550 100644 --- a/packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/tsconfig.json +++ b/packages/adapter-netlify/test/apps/instrumentation/tsconfig.json @@ -1,12 +1,14 @@ { - "extends": "./.svelte-kit/tsconfig.json", "compilerOptions": { "allowJs": true, "checkJs": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, + "skipLibCheck": true, "sourceMap": true, - "noEmit": true - } + "strict": true, + "moduleResolution": "bundler" + }, + "extends": "$app/tsconfig" } diff --git a/packages/adapter-netlify/test/apps/instrumentation/vite.config.ts b/packages/adapter-netlify/test/apps/instrumentation/vite.config.ts new file mode 100644 index 000000000000..8c3bc9c5e4a7 --- /dev/null +++ b/packages/adapter-netlify/test/apps/instrumentation/vite.config.ts @@ -0,0 +1,16 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import type { UserConfig } from 'vite'; +import adapter from '../../../index.js'; + +const config: UserConfig = { + build: { + minify: false + }, + plugins: [ + sveltekit({ + adapter: adapter() + }) + ] +}; + +export default config; diff --git a/packages/adapter-netlify/test/apps/split/.gitignore b/packages/adapter-netlify/test/apps/split/.gitignore index 88f661c765c6..4150a3674e3a 100644 --- a/packages/adapter-netlify/test/apps/split/.gitignore +++ b/packages/adapter-netlify/test/apps/split/.gitignore @@ -3,4 +3,4 @@ node_modules /.svelte-kit /.netlify /build -deno.lock \ No newline at end of file +deno.lock diff --git a/packages/adapter-netlify/test/apps/split/_redirects b/packages/adapter-netlify/test/apps/split/_redirects new file mode 100644 index 000000000000..fd5703a26e6a --- /dev/null +++ b/packages/adapter-netlify/test/apps/split/_redirects @@ -0,0 +1 @@ +/redirect-me /greeting/redirected 301 diff --git a/packages/adapter-netlify/test/apps/split/package.json b/packages/adapter-netlify/test/apps/split/package.json index 2af2c95a4b18..47b5988375bd 100644 --- a/packages/adapter-netlify/test/apps/split/package.json +++ b/packages/adapter-netlify/test/apps/split/package.json @@ -6,7 +6,6 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "prepare": "svelte-kit sync || echo ''", "test:platform": "playwright test" }, "devDependencies": { diff --git a/packages/adapter-netlify/test/apps/split/src/instrumentation.server.js b/packages/adapter-netlify/test/apps/split/src/instrumentation.server.js deleted file mode 100644 index acc9022e1d64..000000000000 --- a/packages/adapter-netlify/test/apps/split/src/instrumentation.server.js +++ /dev/null @@ -1 +0,0 @@ -// this is just here to make sure the changes resulting from it work diff --git a/packages/adapter-netlify/test/apps/split/src/routes/+page.svelte b/packages/adapter-netlify/test/apps/split/src/routes/+page.svelte new file mode 100644 index 000000000000..03c350d48255 --- /dev/null +++ b/packages/adapter-netlify/test/apps/split/src/routes/+page.svelte @@ -0,0 +1 @@ +

Hello from SvelteKit

diff --git a/packages/adapter-netlify/test/apps/split/src/routes/greeting/[name]/+page.server.js b/packages/adapter-netlify/test/apps/split/src/routes/greeting/[name]/+page.server.js new file mode 100644 index 000000000000..31e005a89bad --- /dev/null +++ b/packages/adapter-netlify/test/apps/split/src/routes/greeting/[name]/+page.server.js @@ -0,0 +1,4 @@ +/** @type {import('./$types').PageServerLoad} */ +export function load({ params }) { + return { name: params.name }; +} diff --git a/packages/adapter-netlify/test/apps/split/src/routes/greeting/[name]/+page.svelte b/packages/adapter-netlify/test/apps/split/src/routes/greeting/[name]/+page.svelte new file mode 100644 index 000000000000..4dcbba568369 --- /dev/null +++ b/packages/adapter-netlify/test/apps/split/src/routes/greeting/[name]/+page.svelte @@ -0,0 +1,5 @@ + + +

Hello {data.name}

diff --git a/packages/adapter-netlify/test/apps/split/svelte.config.js b/packages/adapter-netlify/test/apps/split/svelte.config.js deleted file mode 100644 index 82a386bcfc2e..000000000000 --- a/packages/adapter-netlify/test/apps/split/svelte.config.js +++ /dev/null @@ -1,17 +0,0 @@ -import adapter from '../../../index.js'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - compilerOptions: { experimental: { async: true } }, - kit: { - adapter: adapter({ split: true }), - experimental: { - instrumentation: { - server: true - }, - remoteFunctions: true - } - } -}; - -export default config; diff --git a/packages/adapter-netlify/test/apps/split/test/test.js b/packages/adapter-netlify/test/apps/split/test/test.js index 1e502c5c559a..4093bf3fb413 100644 --- a/packages/adapter-netlify/test/apps/split/test/test.js +++ b/packages/adapter-netlify/test/apps/split/test/test.js @@ -1,3 +1,5 @@ +import fs from 'node:fs'; +import path from 'node:path'; import { expect, test } from '@playwright/test'; test('routes to routes with dynamic params', async ({ page }) => { @@ -20,3 +22,17 @@ test('client-side fetch for query remote function data', async ({ page }) => { await page.goto('/remote/query'); await expect(page.locator('p')).toHaveText('a: 1'); }); + +test('split generates multiple function files', () => { + const functions_dir = path.resolve(import.meta.dirname, '../.netlify/v1/functions'); + const files = fs.readdirSync(functions_dir).filter((f) => f.startsWith('sveltekit-')); + expect(files.length).toBeGreaterThan(1); +}); + +test('_redirects are copied to publish directory', () => { + const redirects = fs.readFileSync( + path.resolve(import.meta.dirname, '../build/_redirects'), + 'utf-8' + ); + expect(redirects).toContain('/redirect-me /greeting/redirected 301'); +}); diff --git a/packages/adapter-netlify/test/apps/split/tsconfig.json b/packages/adapter-netlify/test/apps/split/tsconfig.json index c2db702f9bb1..0c2092500732 100644 --- a/packages/adapter-netlify/test/apps/split/tsconfig.json +++ b/packages/adapter-netlify/test/apps/split/tsconfig.json @@ -9,5 +9,5 @@ "sourceMap": true, "moduleResolution": "bundler" }, - "extends": "./.svelte-kit/tsconfig.json" + "extends": "$app/tsconfig" } diff --git a/packages/adapter-netlify/test/apps/split/vite.config.ts b/packages/adapter-netlify/test/apps/split/vite.config.ts index 72a307cc0e56..5f565edb7cf6 100644 --- a/packages/adapter-netlify/test/apps/split/vite.config.ts +++ b/packages/adapter-netlify/test/apps/split/vite.config.ts @@ -1,11 +1,20 @@ import { sveltekit } from '@sveltejs/kit/vite'; import type { UserConfig } from 'vite'; +import adapter from '../../../index.js'; const config: UserConfig = { build: { minify: false }, - plugins: [sveltekit()] + plugins: [ + sveltekit({ + adapter: adapter({ split: true }), + compilerOptions: { experimental: { async: true } }, + experimental: { + remoteFunctions: true + } + }) + ] }; export default config; diff --git a/packages/adapter-netlify/test/utils.js b/packages/adapter-netlify/test/utils.js index 18c9c56d89d7..b5bdbb0a952e 100644 --- a/packages/adapter-netlify/test/utils.js +++ b/packages/adapter-netlify/test/utils.js @@ -16,7 +16,7 @@ export const config = { baseURL: process.env.DEPLOYMENT_URL, screenshot: 'only-on-failure', trace: 'retain-on-failure', - channel: 'chromium' + channel: 'chrome' }, workers: process.env.CI ? 2 : undefined, reporter: 'list', diff --git a/packages/adapter-netlify/tsconfig.json b/packages/adapter-netlify/tsconfig.json index 6a110982ab66..ed0b586c30f6 100644 --- a/packages/adapter-netlify/tsconfig.json +++ b/packages/adapter-netlify/tsconfig.json @@ -11,5 +11,5 @@ }, "types": ["node"] }, - "include": ["*.js", "src/**/*.js", "internal.d.ts", "test/utils.js"] + "include": ["*.js", "files/serverless.js", "internal.d.ts", "test/utils.js"] } diff --git a/packages/adapter-netlify/utils.js b/packages/adapter-netlify/utils.js new file mode 100644 index 000000000000..7c90fea67e87 --- /dev/null +++ b/packages/adapter-netlify/utils.js @@ -0,0 +1,78 @@ +import { resolve } from 'node:path'; +import process from 'node:process'; + +/** + * @typedef {{ rest: boolean, dynamic: boolean, content: string }} RouteSegment + */ + +/** + * @typedef {{ + * build?: { publish?: string } + * functions?: { node_bundler?: 'zisi' | 'esbuild' } + * }} NetlifyConfig + */ + +/** + * @param {RouteSegment[]} a + * @param {RouteSegment[]} b + * @returns {boolean} + */ +export function matches(a, b) { + if (a[0] && b[0]) { + if (b[0].rest) { + if (b.length === 1) return true; + + const next_b = b.slice(1); + + for (let i = 0; i < a.length; i += 1) { + if (matches(a.slice(i), next_b)) return true; + } + + return false; + } + + if (!b[0].dynamic) { + if (!a[0].dynamic && a[0].content !== b[0].content) return false; + } + + if (a.length === 1 && b.length === 1) return true; + return matches(a.slice(1), b.slice(1)); + } else if (a[0]) { + return a.length === 1 && a[0].rest; + } else { + return b.length === 1 && b[0].rest; + } +} + +/** + * @param {NetlifyConfig | null} netlify_config + * @param {import('@sveltejs/kit').Builder} builder + * @returns {string | undefined} + */ +export function get_publish_directory(netlify_config, builder) { + if (netlify_config) { + if (!netlify_config.build?.publish) { + builder.log.minor('No publish directory specified in netlify.toml, using default'); + return; + } + + if (resolve(netlify_config.build.publish) === process.cwd()) { + throw new Error( + 'The publish directory cannot be set to the site root. Please change it to another value such as "build" in netlify.toml.' + ); + } + return netlify_config.build.publish; + } + + builder.log.warn( + 'No netlify.toml found. Using default publish directory. Consult https://svelte.dev/docs/kit/adapter-netlify#usage for more details' + ); +} + +/** + * @param {*} value + * @returns {string} + */ +export function s(value) { + return JSON.stringify(value, null, '\t'); +} diff --git a/packages/adapter-netlify/utils.spec.js b/packages/adapter-netlify/utils.spec.js new file mode 100644 index 000000000000..60da6beda5cd --- /dev/null +++ b/packages/adapter-netlify/utils.spec.js @@ -0,0 +1,161 @@ +import process from 'node:process'; +import { describe, test, expect, vi } from 'vitest'; +import { matches, get_publish_directory } from './utils.js'; + +/** + * Helper to create a static route segment + * @param {string} content + * @returns {{ rest: boolean, dynamic: boolean, content: string }} + */ +function static_segment(content) { + return { rest: false, dynamic: false, content }; +} + +/** + * Helper to create a dynamic route segment + * @param {string} content + * @returns {{ rest: boolean, dynamic: boolean, content: string }} + */ +function dynamic_segment(content) { + return { rest: false, dynamic: true, content }; +} + +/** + * Helper to create a rest route segment + * @param {string} content + * @returns {{ rest: boolean, dynamic: boolean, content: string }} + */ +function rest_segment(content) { + return { rest: true, dynamic: true, content }; +} + +describe('matches', () => { + test('two identical static routes match', () => { + expect( + matches( + [static_segment('blog'), static_segment('post')], + [static_segment('blog'), static_segment('post')] + ) + ).toBe(true); + }); + + test('static segment mismatch returns false', () => { + expect(matches([static_segment('blog')], [static_segment('about')])).toBe(false); + }); + + test('dynamic segment matches any static segment', () => { + expect(matches([static_segment('blog')], [dynamic_segment('[slug]')])).toBe(true); + }); + + test('rest segment at end matches everything', () => { + expect( + matches([static_segment('blog'), static_segment('post')], [rest_segment('[...rest]')]) + ).toBe(true); + }); + + test('rest-only route matches any route', () => { + expect( + matches( + [static_segment('a'), static_segment('b'), static_segment('c')], + [rest_segment('[...rest]')] + ) + ).toBe(true); + }); + + test('rest segment matches remaining segments', () => { + expect( + matches( + [static_segment('blog'), static_segment('2024'), static_segment('post')], + [static_segment('blog'), rest_segment('[...rest]')] + ) + ).toBe(true); + }); + + test('empty segments (index routes)', () => { + expect(matches([], [])).toBe(false); + }); + + test('mismatched lengths without rest return false', () => { + expect( + matches([static_segment('blog'), static_segment('post')], [static_segment('blog')]) + ).toBe(false); + }); + + test('rest with following segments', () => { + expect( + matches( + [static_segment('a'), static_segment('b'), static_segment('page')], + [rest_segment('[...rest]'), static_segment('page')] + ) + ).toBe(true); + }); + + test('rest with following segments that do not match', () => { + expect( + matches( + [static_segment('a'), static_segment('b'), static_segment('other')], + [rest_segment('[...rest]'), static_segment('page')] + ) + ).toBe(false); + }); + + test('a has trailing rest and b is shorter', () => { + expect(matches([rest_segment('[...rest]')], [static_segment('blog')])).toBe(true); + }); + + test('a is longer without rest in b', () => { + expect(matches([static_segment('a'), static_segment('b')], [static_segment('a')])).toBe(false); + }); + + test('b is longer without rest in a', () => { + expect(matches([static_segment('a')], [static_segment('a'), static_segment('b')])).toBe(false); + }); + + test('dynamic in a matches static in b', () => { + expect(matches([dynamic_segment('[id]')], [static_segment('blog')])).toBe(true); + }); + + test('both dynamic segments match', () => { + expect(matches([dynamic_segment('[id]')], [dynamic_segment('[slug]')])).toBe(true); + }); +}); + +describe('get_publish_directory', () => { + test('returns undefined when no netlify.toml, with warning logged', () => { + const warn = vi.fn(); + const builder = /** @type {any} */ ({ log: { warn, minor: vi.fn() } }); + + const result = get_publish_directory(null, builder); + + expect(result).toBeUndefined(); + expect(warn).toHaveBeenCalledOnce(); + expect(warn).toHaveBeenCalledWith(expect.stringContaining('No netlify.toml found')); + }); + + test('returns undefined when config has no build.publish, with minor log', () => { + const minor = vi.fn(); + const builder = /** @type {any} */ ({ log: { warn: vi.fn(), minor } }); + + const result = get_publish_directory({ build: {} }, builder); + + expect(result).toBeUndefined(); + expect(minor).toHaveBeenCalledOnce(); + expect(minor).toHaveBeenCalledWith(expect.stringContaining('No publish directory specified')); + }); + + test('returns the publish value when specified', () => { + const builder = /** @type {any} */ ({ log: { warn: vi.fn(), minor: vi.fn() } }); + + const result = get_publish_directory({ build: { publish: 'dist' } }, builder); + + expect(result).toBe('dist'); + }); + + test('throws when publish is site root', () => { + const builder = /** @type {any} */ ({ log: { warn: vi.fn(), minor: vi.fn() } }); + + expect(() => get_publish_directory({ build: { publish: process.cwd() } }, builder)).toThrow( + 'The publish directory cannot be set to the site root' + ); + }); +}); diff --git a/packages/adapter-netlify/vitest.config.js b/packages/adapter-netlify/vitest.config.js new file mode 100644 index 000000000000..a15b3a470a6d --- /dev/null +++ b/packages/adapter-netlify/vitest.config.js @@ -0,0 +1,5 @@ +// we need this file to prevent Vitest from resolving a Vitest config from another directory + +import { defineConfig } from 'vitest/config'; + +export default defineConfig({}); diff --git a/packages/adapter-node/CHANGELOG.md b/packages/adapter-node/CHANGELOG.md index 606f5ab44bbe..b1cf97693e6c 100644 --- a/packages/adapter-node/CHANGELOG.md +++ b/packages/adapter-node/CHANGELOG.md @@ -1,5 +1,141 @@ # @sveltejs/adapter-node +## 6.0.0-next.10 + +### Patch Changes + +- chore: bump `rolldown` to 1.2.3 ([#16745](https://github.com/sveltejs/kit/pull/16745)) +- Updated dependencies [[`1742811`](https://github.com/sveltejs/kit/commit/1742811562358caf6042bed33c60ce6427a3e831), [`1611c61`](https://github.com/sveltejs/kit/commit/1611c617a1ec1b32e10627c2815fb7e6673f3157), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`13e7b18`](https://github.com/sveltejs/kit/commit/13e7b186a94b5b8317771d2b1e795b432dcd6faf), [`529346d`](https://github.com/sveltejs/kit/commit/529346d96369f92cdd804be9c4313c598efe34b8), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`81d6319`](https://github.com/sveltejs/kit/commit/81d6319eef08a765ccd3922790a7aee16e284cc2), [`b361b81`](https://github.com/sveltejs/kit/commit/b361b815e79828a6b7404d759d0a4c3892814a4e), [`69a5bdf`](https://github.com/sveltejs/kit/commit/69a5bdf9f45810eeed2faa089c8d3206463d347e)]: + - @sveltejs/kit@3.0.0-next.20 + +## 6.0.0-next.9 + +### Patch Changes + +- fix: externalize `@opentelemetry/api` to prevent bundler chunk colocation between `instrumentation.server.js` and application code ([#16302](https://github.com/sveltejs/kit/pull/16302)) +- Updated dependencies [[`04f9ab4`](https://github.com/sveltejs/kit/commit/04f9ab4f64a25c2fecac65ccd97cf4663eb9c1fa), [`1e198bd`](https://github.com/sveltejs/kit/commit/1e198bdbea745b845d9ae59e6a2d3fc62a404b01), [`813726d`](https://github.com/sveltejs/kit/commit/813726dabdc923ce138452dc8cc91d881e329e5d), [`a115a7b`](https://github.com/sveltejs/kit/commit/a115a7beb9b34d1e43609e96100de48010afc3be), [`031ac69`](https://github.com/sveltejs/kit/commit/031ac69d0d8c7d66c9e61aa43a8c447877eb87ff), [`dc7442c`](https://github.com/sveltejs/kit/commit/dc7442cc622c257e89a7acc9253795a449d51df7), [`19c4478`](https://github.com/sveltejs/kit/commit/19c447850e269807eeb089b595267a0616203afa), [`dc7442c`](https://github.com/sveltejs/kit/commit/dc7442cc622c257e89a7acc9253795a449d51df7), [`dc7442c`](https://github.com/sveltejs/kit/commit/dc7442cc622c257e89a7acc9253795a449d51df7), [`0b3e2b3`](https://github.com/sveltejs/kit/commit/0b3e2b386896cf868328138a66670f19931ebea5), [`c5d0ce2`](https://github.com/sveltejs/kit/commit/c5d0ce2a9e47615c245e8ac57e10ee2120142138)]: + - @sveltejs/kit@3.0.0-next.19 + +## 6.0.0-next.8 + +### Major Changes + +- chore: migrate from rollup to rolldown + +- breaking: require SvelteKit 3 + +- breaking: add `kit.paths.origin` config option, remove `kit.prerender.origin` and the `adapter-node` `ORIGIN` environment variable + +- breaking: require `vite@^8.0.12`, the first Vite 8 release bundling stable `rolldown` 1.0.0 + +### Minor Changes + +- feat: better error logging + +### Patch Changes + +- fix: serve static files with the Content-Type recorded in the manifest + +- chore: use `node:fs` instead of deprecated `builder.rimraf` and `builder.mkdirp` ([#16286](https://github.com/sveltejs/kit/pull/16286)) + +- fix: allow prerelease versions of SvelteKit 3 to satisfy the peer dependency range + +- fix: correctly bundle entrypoints on Windows + +- fix: preserve stripped path prefixes by making trailing-slash redirects relative + +- chore: bump Rolldown to `1.2.0` + +- fix: don't send `Vary: Accept-Encoding` for assets that were never precompressed +- Updated dependencies [[`bbd1e2f`](https://github.com/sveltejs/kit/commit/bbd1e2fcc9af58edb9846393b081be61df4789d5), [`e21d27b`](https://github.com/sveltejs/kit/commit/e21d27ba490c89da98dbad24fca364d0163137c4), [`6044d05`](https://github.com/sveltejs/kit/commit/6044d055031bc17deedc069dc4016565364bd4f4), [`7fb611f`](https://github.com/sveltejs/kit/commit/7fb611fb604e4d74cd889f5fe21ccd255f876382), [`9f06b3a`](https://github.com/sveltejs/kit/commit/9f06b3ac78be30d27b07fc00ea40bda493c573aa), [`8763327`](https://github.com/sveltejs/kit/commit/87633271ce40ac0839be9468598e965bde3cad51), [`7dc2379`](https://github.com/sveltejs/kit/commit/7dc23798cde97b5616049ba8d443a07e8c558bdc), [`a70c3eb`](https://github.com/sveltejs/kit/commit/a70c3eb31fe1e28f1532c84ec569ec7099da3344), [`89f49c7`](https://github.com/sveltejs/kit/commit/89f49c70b7fb4883cf704d2fb57d2d8595790ad2), [`f36b3a4`](https://github.com/sveltejs/kit/commit/f36b3a4cd307bd8c89e03fa191becc175e7b3077), [`1144a63`](https://github.com/sveltejs/kit/commit/1144a63d318c3c3255d06b374bb7a656a469fabf), [`9f06b3a`](https://github.com/sveltejs/kit/commit/9f06b3ac78be30d27b07fc00ea40bda493c573aa)]: + - @sveltejs/kit@3.0.0-next.15 + +## 6.0.0-next.7 + +### Patch Changes + +- fix: serve static files with the Content-Type recorded in the manifest ([#16564](https://github.com/sveltejs/kit/pull/16564)) + +- fix: don't send `Vary: Accept-Encoding` for assets that were never precompressed ([#16566](https://github.com/sveltejs/kit/pull/16566)) +- Updated dependencies [[`c3f58bb`](https://github.com/sveltejs/kit/commit/c3f58bb0eb6bde790897f1e8b89019bc39a062e5), [`b655dc7`](https://github.com/sveltejs/kit/commit/b655dc7cf043ff01d53e494e9744e548bb3ad9fd), [`a70cc4f`](https://github.com/sveltejs/kit/commit/a70cc4f2ca24e7099c898456085ffe18d191657e), [`b325afb`](https://github.com/sveltejs/kit/commit/b325afb509b54445f678348e88ea2bf0152f4cec), [`e76b3d7`](https://github.com/sveltejs/kit/commit/e76b3d778bacb429fdb40a41b13ff04b54d97c1f), [`58f47eb`](https://github.com/sveltejs/kit/commit/58f47eb94016c5405829667bbc873e027c7b363c), [`fe9d8d9`](https://github.com/sveltejs/kit/commit/fe9d8d939b6d1a07b2957104fda6a1123f5eebd3), [`0c7bbd7`](https://github.com/sveltejs/kit/commit/0c7bbd7bd4a346c560e7cbf9b39ba8bda7c718f0), [`08d7e2a`](https://github.com/sveltejs/kit/commit/08d7e2a1b327d4e68c217f6e7d6b305732c82b1b), [`f70f679`](https://github.com/sveltejs/kit/commit/f70f679143d4330b6d3b7d0e690145b5487df97f), [`0624b26`](https://github.com/sveltejs/kit/commit/0624b2678efd41223f70972f7bff2d81d4a5c7f9), [`0624b26`](https://github.com/sveltejs/kit/commit/0624b2678efd41223f70972f7bff2d81d4a5c7f9), [`f4222ad`](https://github.com/sveltejs/kit/commit/f4222adc504a7041b50325424a3eb86af02961ed), [`089628b`](https://github.com/sveltejs/kit/commit/089628b0d9637578085ad61b5786cd1c4476e399), [`d8617b3`](https://github.com/sveltejs/kit/commit/d8617b3f2f08144f6acfc7c636b89d62b982a33d), [`c7369a1`](https://github.com/sveltejs/kit/commit/c7369a148169dfd51af150c461ff266977b6befd), [`5e5e592`](https://github.com/sveltejs/kit/commit/5e5e592f093d05c6ea30a41a0ebf543829069c5f), [`60057c1`](https://github.com/sveltejs/kit/commit/60057c1d2a1e6429e5f315d587ebc01baef6dc94), [`f70f679`](https://github.com/sveltejs/kit/commit/f70f679143d4330b6d3b7d0e690145b5487df97f), [`38f1528`](https://github.com/sveltejs/kit/commit/38f15289e249942275eab1281516702de08b7a30), [`40092f3`](https://github.com/sveltejs/kit/commit/40092f34aa063d033fc7a4d1d6fb1c10abd8cbf6), [`ff4247e`](https://github.com/sveltejs/kit/commit/ff4247e2376862207dd9df7317f39d8bb8dd9ee4), [`79a5de5`](https://github.com/sveltejs/kit/commit/79a5de581fc1bb708879678b8295706221ba0f0f), [`a70cc4f`](https://github.com/sveltejs/kit/commit/a70cc4f2ca24e7099c898456085ffe18d191657e)]: + - @sveltejs/kit@3.0.0-next.14 + +## 6.0.0-next.6 + +### Patch Changes + +- fix: preserve stripped path prefixes by making trailing-slash redirects relative ([#16431](https://github.com/sveltejs/kit/pull/16431)) + +- chore: bump Rolldown to `1.2.0` ([#16455](https://github.com/sveltejs/kit/pull/16455)) +- Updated dependencies [[`adc4c5b`](https://github.com/sveltejs/kit/commit/adc4c5be542ecf7da93ee8d0e90e9d13b952998b), [`ecb0701`](https://github.com/sveltejs/kit/commit/ecb0701b8826e41229f84922f37c12099c66d3bc), [`4b4cc60`](https://github.com/sveltejs/kit/commit/4b4cc60990fa940236639a6cecb6c8efe4287bfa), [`7390dbe`](https://github.com/sveltejs/kit/commit/7390dbe6ef8dff0a1cd923f96daef462a3ce68f2), [`2e71340`](https://github.com/sveltejs/kit/commit/2e713407deb22965777afb01cb8215013ef8bfd3), [`c87bc3a`](https://github.com/sveltejs/kit/commit/c87bc3ac7830f8ee4e76254c9315adcc79ff6a99), [`af15c6c`](https://github.com/sveltejs/kit/commit/af15c6cab2fbfe6bae51dc1fa77a9271df996a47), [`c6fa431`](https://github.com/sveltejs/kit/commit/c6fa43157ae0dbaa9fc61b7e8d2db673e1f9f893), [`bfe4dea`](https://github.com/sveltejs/kit/commit/bfe4deae006f61347b5a5dce7747ec96692e97f9), [`df7dc72`](https://github.com/sveltejs/kit/commit/df7dc7219451bcc86a93e93188d78d664973d9ae), [`5ae11a1`](https://github.com/sveltejs/kit/commit/5ae11a119667c1b24e3b6d17f4b538032ddf1ab1), [`781205c`](https://github.com/sveltejs/kit/commit/781205c41cc5696eb862e5c13356c301a0368983), [`c87bc3a`](https://github.com/sveltejs/kit/commit/c87bc3ac7830f8ee4e76254c9315adcc79ff6a99), [`a1bfeb9`](https://github.com/sveltejs/kit/commit/a1bfeb9de906ae140b9cdc7d7eeaf0ee652a7bcc), [`c87bc3a`](https://github.com/sveltejs/kit/commit/c87bc3ac7830f8ee4e76254c9315adcc79ff6a99), [`9f0127d`](https://github.com/sveltejs/kit/commit/9f0127d4eee3f17dc1db1d2b99b1ca5fc80af784), [`4b4cc60`](https://github.com/sveltejs/kit/commit/4b4cc60990fa940236639a6cecb6c8efe4287bfa), [`8f5b9c7`](https://github.com/sveltejs/kit/commit/8f5b9c72ae930b3b93ef9e1b9ecb9e52664e1d74), [`c9b5544`](https://github.com/sveltejs/kit/commit/c9b554448633e1839e8c0f16c374b00a5ce4d1b6), [`fe1d4a4`](https://github.com/sveltejs/kit/commit/fe1d4a4d68d1d7a3925004fe804209ff54091e34), [`4b4cc60`](https://github.com/sveltejs/kit/commit/4b4cc60990fa940236639a6cecb6c8efe4287bfa), [`5f78e95`](https://github.com/sveltejs/kit/commit/5f78e953ae3966c23a01194dd200f5c57f723e40), [`25510fc`](https://github.com/sveltejs/kit/commit/25510fc79484363277d062e01f2b872f17a473f5), [`c99a6cf`](https://github.com/sveltejs/kit/commit/c99a6cfb7fec133aa85a6de59b43fc442aedc41a), [`17a45ca`](https://github.com/sveltejs/kit/commit/17a45cadb9356403b82c170f0d0027a1ba085667), [`2ca20c3`](https://github.com/sveltejs/kit/commit/2ca20c3ee7348836757dc6f3469217e46957766b), [`eb5c973`](https://github.com/sveltejs/kit/commit/eb5c9737b6d74c53f572b3e047faa5947052ced2)]: + - @sveltejs/kit@3.0.0-next.12 + +## 6.0.0-next.5 + +### Minor Changes + +- feat: better error logging ([#16374](https://github.com/sveltejs/kit/pull/16374)) + +### Patch Changes + +- Updated dependencies [[`5220191`](https://github.com/sveltejs/kit/commit/5220191a3bac885fb1474718afee03dafa6b0ce7), [`8cb2f7d`](https://github.com/sveltejs/kit/commit/8cb2f7d007af7e53f780d5b8dbbfe87459befd41), [`b88c7a7`](https://github.com/sveltejs/kit/commit/b88c7a7ba07c0a9309598aebcb86709f8a09c443), [`a6ea113`](https://github.com/sveltejs/kit/commit/a6ea1135fd1a9f0ec5dcc2625215331c55ff8a02), [`6446f64`](https://github.com/sveltejs/kit/commit/6446f64b755ce6489f66c71961e43ad0d89e84eb), [`58f1789`](https://github.com/sveltejs/kit/commit/58f178999e172820bcb278be0c61e7f97b0396ac), [`09774a2`](https://github.com/sveltejs/kit/commit/09774a27004853ecb5140fde9f40356b9bffb077), [`c542fdd`](https://github.com/sveltejs/kit/commit/c542fdda394cff4b3709ddb895417b8a14ab7d30), [`aedaa27`](https://github.com/sveltejs/kit/commit/aedaa27be9df0882e54952b443985197a1e0a16e), [`428ee1a`](https://github.com/sveltejs/kit/commit/428ee1ae27543caa483c9532457b1855dbc10683), [`fefb3ae`](https://github.com/sveltejs/kit/commit/fefb3ae495cc1ec10e35be988b2878bfd880a62e)]: + - @sveltejs/kit@3.0.0-next.11 + +## 6.0.0-next.4 + +### Patch Changes + +- fix: correctly bundle entrypoints on Windows ([#16367](https://github.com/sveltejs/kit/pull/16367)) +- Updated dependencies [[`c1ee782`](https://github.com/sveltejs/kit/commit/c1ee7829b0c7ec9a874c86d7fd1e952f875f5bed), [`1a1b3ea`](https://github.com/sveltejs/kit/commit/1a1b3ea53bdff1d83c4f22e934e825810a088688), [`6423d98`](https://github.com/sveltejs/kit/commit/6423d9827cd31dd7b26c4c611dbabe6d636fc91c), [`6d1f4f0`](https://github.com/sveltejs/kit/commit/6d1f4f0cea991c532cfad49eec8662dac31dfe2b), [`5ca9906`](https://github.com/sveltejs/kit/commit/5ca990646d886d184216995b01d10f5cc649ceed), [`b148d31`](https://github.com/sveltejs/kit/commit/b148d31398f6c0219d553a172d8cf46223d5db53), [`5ca9906`](https://github.com/sveltejs/kit/commit/5ca990646d886d184216995b01d10f5cc649ceed), [`9f3d9bb`](https://github.com/sveltejs/kit/commit/9f3d9bbb0c63e7197c23f7529565542c2d77e592), [`7bfd922`](https://github.com/sveltejs/kit/commit/7bfd922b367571426dcc5c573317bab8beac35f6), [`ffa0e3b`](https://github.com/sveltejs/kit/commit/ffa0e3b06e82d7023dd2189ccfe1fc191c90b941)]: + - @sveltejs/kit@3.0.0-next.9 + +## 6.0.0-next.3 + +### Patch Changes + +- fix: allow prerelease versions of SvelteKit 3 to satisfy the peer dependency range ([#16286](https://github.com/sveltejs/kit/pull/16286)) +- Updated dependencies [[`737d119`](https://github.com/sveltejs/kit/commit/737d119730ec163832f838b3813c9cbb75dd799f), [`fa78efb`](https://github.com/sveltejs/kit/commit/fa78efb626031497eecc3a88fa7b6f6da36eca0f), [`07c207e`](https://github.com/sveltejs/kit/commit/07c207ed919570d4ed4954f751056b4f838abdea), [`a47071b`](https://github.com/sveltejs/kit/commit/a47071bb6581fe5f6df4cc47b80e9f0f594624e5), [`14d7d5a`](https://github.com/sveltejs/kit/commit/14d7d5abd9cd8462e4c79ed6a5c10bc43258e97a), [`5c38e51`](https://github.com/sveltejs/kit/commit/5c38e515db7fbb92e5ae01db84b4f0040a02f187), [`0702baa`](https://github.com/sveltejs/kit/commit/0702baa58b2a4600e83b55ef4a85e9cbe3391a4f), [`e1938c6`](https://github.com/sveltejs/kit/commit/e1938c62348b8ccc83e186b1acad76a0a10cc9ea), [`8293144`](https://github.com/sveltejs/kit/commit/8293144b6fbfff9f97cdb1d2224a5eaa04c84fc4), [`f76d7d9`](https://github.com/sveltejs/kit/commit/f76d7d91e18a0afd1f6798c095eb8c170c108cff), [`ab5c253`](https://github.com/sveltejs/kit/commit/ab5c253efb2546ead74db4c8c9b4c7639d97e0ed), [`b557b1b`](https://github.com/sveltejs/kit/commit/b557b1ba76903e1fb0fc3bb87bddc5420332137a), [`4a513e2`](https://github.com/sveltejs/kit/commit/4a513e27ab2ad44dae0c6a4a6c8deda840008530)]: + - @sveltejs/kit@3.0.0-next.8 + +## 6.0.0-next.2 +### Major Changes + + +- breaking: add `kit.paths.origin` config option, remove `kit.prerender.origin` and the `adapter-node` `ORIGIN` environment variable ([#16161](https://github.com/sveltejs/kit/pull/16161)) + + +### Patch Changes + +- Updated dependencies [[`3c434fb`](https://github.com/sveltejs/kit/commit/3c434fbf12822e19bea833a2084b329590ccf5b1), [`a9284e8`](https://github.com/sveltejs/kit/commit/a9284e8e627b1b0a8d3772c53310c41056e556b1), [`3726a7a`](https://github.com/sveltejs/kit/commit/3726a7a67c949a160283df7db6033b440e229171), [`f9d2240`](https://github.com/sveltejs/kit/commit/f9d22409aa0fca0f5035aead811fe6907f63c293), [`a9284e8`](https://github.com/sveltejs/kit/commit/a9284e8e627b1b0a8d3772c53310c41056e556b1), [`7c040ba`](https://github.com/sveltejs/kit/commit/7c040ba28e98ecbf38acbbf93653fc6c33a506ba), [`223eaad`](https://github.com/sveltejs/kit/commit/223eaadca55c6e73096cd605759d6c7366f260a2), [`223eaad`](https://github.com/sveltejs/kit/commit/223eaadca55c6e73096cd605759d6c7366f260a2), [`3b907d4`](https://github.com/sveltejs/kit/commit/3b907d449a42f9d763cf8eaa6ac5a88975137011), [`fd628a5`](https://github.com/sveltejs/kit/commit/fd628a57348d25fb433f63778e0239892ebb1da2), [`223eaad`](https://github.com/sveltejs/kit/commit/223eaadca55c6e73096cd605759d6c7366f260a2), [`6c1d035`](https://github.com/sveltejs/kit/commit/6c1d03535b09f670164eb22f05405c17120149f1), [`178eac0`](https://github.com/sveltejs/kit/commit/178eac007e648f3235bc45d926cfc029ddf8de07), [`c6562a9`](https://github.com/sveltejs/kit/commit/c6562a92cb908058cab3fabad075a94ad9cc6e39), [`8eca2ab`](https://github.com/sveltejs/kit/commit/8eca2ab8b4760456a782d7a29f83f24a15fac177), [`61cf188`](https://github.com/sveltejs/kit/commit/61cf188e38725b041d0411c6074ad2649a7c42b4)]: + - @sveltejs/kit@3.0.0-next.6 + +## 6.0.0-next.1 +### Major Changes + + +- breaking: require `vite@^8.0.12`, the first Vite 8 release bundling stable `rolldown` 1.0.0 ([#16134](https://github.com/sveltejs/kit/pull/16134)) + + +### Patch Changes + +- Updated dependencies [[`a9629f1`](https://github.com/sveltejs/kit/commit/a9629f136d3256b14a8b3c10114c99832b977f6c), [`53d37f9`](https://github.com/sveltejs/kit/commit/53d37f93cf1f46995264398d4e54bb27bd1e75dd), [`dba6239`](https://github.com/sveltejs/kit/commit/dba62390e96f9f40c55ddc203744138664742026), [`ab12cb6`](https://github.com/sveltejs/kit/commit/ab12cb6a07269e2a18c0a3867e608e9838c592f3), [`f0eab52`](https://github.com/sveltejs/kit/commit/f0eab522bdea2d2a00f9b5473cd917d678b20daa), [`cd884c1`](https://github.com/sveltejs/kit/commit/cd884c1ffa6f96e08c70486c52fcca20501d5cef), [`9ed38a8`](https://github.com/sveltejs/kit/commit/9ed38a886be8c247c42c887c15d53b405693e1a9), [`53aa049`](https://github.com/sveltejs/kit/commit/53aa049a06ce7b053bdbe6484f05eecdb09d874e), [`984e57a`](https://github.com/sveltejs/kit/commit/984e57ac95d6ca7911611e1c297eecd1b1d870db), [`51785be`](https://github.com/sveltejs/kit/commit/51785be17ac706659d7fd7b3fca3fcf58dcc5de0), [`4f6bcbb`](https://github.com/sveltejs/kit/commit/4f6bcbbbb6e104238c41172ce7dbdaba486bbf43), [`78e7137`](https://github.com/sveltejs/kit/commit/78e71372ca284160df0aefe407999d46795a1299)]: + - @sveltejs/kit@3.0.0-next.5 + +## 6.0.0-next.0 +### Major Changes + + +- chore: migrate from rollup to rolldown ([#15297](https://github.com/sveltejs/kit/pull/15297)) + + +- breaking: require SvelteKit 3 ([#15506](https://github.com/sveltejs/kit/pull/15506)) + + +### Patch Changes + +- Updated dependencies [[`fa335bd`](https://github.com/sveltejs/kit/commit/fa335bd8af3a982d68c4d34b813b68887dc7cce8), [`3031d89`](https://github.com/sveltejs/kit/commit/3031d89087d49297e89b6566108a1a1105c38475), [`cb9d416`](https://github.com/sveltejs/kit/commit/cb9d416d05266b6237f2856fb94df5b8a9b04279), [`caf3a18`](https://github.com/sveltejs/kit/commit/caf3a18de6581ab7d454d6fa8ad37ebdbdef95e6), [`4777827`](https://github.com/sveltejs/kit/commit/4777827246727045cf0209399751377564e7951d), [`a2792e2`](https://github.com/sveltejs/kit/commit/a2792e2cdc2dee91c20c7bfd921a104b539abae5), [`ba36148`](https://github.com/sveltejs/kit/commit/ba36148577ed0bc2675034a3d57001d71d328cd1), [`48e8710`](https://github.com/sveltejs/kit/commit/48e8710889b348a039322e05f3d2672327d56184), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`047d6a0`](https://github.com/sveltejs/kit/commit/047d6a0f1c3fd1bc6f90a81177a2834b023a64b3), [`87603d1`](https://github.com/sveltejs/kit/commit/87603d13d009cada171c9b5d5daad5adad4f72de), [`096962c`](https://github.com/sveltejs/kit/commit/096962c47b0b0f63acd0d539d3973797e4867487), [`d545970`](https://github.com/sveltejs/kit/commit/d54597018308068aea5268e6c45b26398cd57607), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`d06affc`](https://github.com/sveltejs/kit/commit/d06affc9246c676fc32b644efc0bbf53b42bc67d), [`8af47eb`](https://github.com/sveltejs/kit/commit/8af47eb06f9a1a883058de059ccf81aab4e85627), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`5c4d130`](https://github.com/sveltejs/kit/commit/5c4d130ea2953464d8b4b462eb5aa4641aa78da1), [`3f11f35`](https://github.com/sveltejs/kit/commit/3f11f35125c5b3625891f0d00d9c7c826e6eaea2), [`caf3a18`](https://github.com/sveltejs/kit/commit/caf3a18de6581ab7d454d6fa8ad37ebdbdef95e6), [`8823037`](https://github.com/sveltejs/kit/commit/8823037b4a341013cc40a9bb688ee23bda3dbb04), [`1d76212`](https://github.com/sveltejs/kit/commit/1d76212740f59ed58839ef569899c653870834fd), [`0dc0548`](https://github.com/sveltejs/kit/commit/0dc0548307a439b6a6f24b14405366ced213f828), [`00d81fa`](https://github.com/sveltejs/kit/commit/00d81fab618435bac95b1bacb5e9734b61ba1af6)]: + - @sveltejs/kit@3.0.0-next.0 + ## 5.5.7 ### Patch Changes diff --git a/packages/adapter-node/index.d.ts b/packages/adapter-node/index.d.ts index 03422f6bfdf3..71daeab1361c 100644 --- a/packages/adapter-node/index.d.ts +++ b/packages/adapter-node/index.d.ts @@ -1,6 +1,12 @@ import { Adapter } from '@sveltejs/kit'; import './ambient.js'; +declare global { + const ENV_PREFIX: string; + const PRECOMPRESS: boolean; + const ORIGIN: string | undefined; +} + interface AdapterOptions { /** * The directory to build the server to. It defaults to `build` — i.e. @@ -25,7 +31,6 @@ interface AdapterOptions { * ```sh * MY_CUSTOM_HOST=127.0.0.1 \ * MY_CUSTOM_PORT=4000 \ - * MY_CUSTOM_ORIGIN=https://my.site \ * node build * ``` */ diff --git a/packages/adapter-node/index.js b/packages/adapter-node/index.js index 3d0c3753e35a..4ce90cf944c7 100644 --- a/packages/adapter-node/index.js +++ b/packages/adapter-node/index.js @@ -1,133 +1,167 @@ -import { readFileSync } from 'node:fs'; +import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { extname } from 'node:path'; import { fileURLToPath } from 'node:url'; -import { rollup } from 'rollup'; -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import json from '@rollup/plugin-json'; -import replace from '@rollup/plugin-replace'; - -/** - * @template T - * @template {keyof T} K - * @typedef {Partial> & Required>} PartialExcept - */ - -/** - * We use a custom `Builder` type here to support the minimum version of SvelteKit. - * @typedef {PartialExcept} Builder2_4_0 - */ +import { rolldown } from 'rolldown'; const files = fileURLToPath(new URL('./files', import.meta.url).href); -/** @type {import('./index.js').default} */ +/** @param {string} str */ +function escape_regex(str) { + // TODO replace with `RegExp.escape(str)` when we require Node >= 24 + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +/** @type {typeof import('./index.js').default} */ export default function (opts = {}) { const { out = 'build', precompress = true, envPrefix = '' } = opts; return { name: '@sveltejs/adapter-node', - /** @param {Builder2_4_0} builder */ async adapt(builder) { const tmp = builder.getBuildDirectory('adapter-node'); - builder.rimraf(out); - builder.rimraf(tmp); - builder.mkdirp(tmp); + rmSync(out, { force: true, recursive: true }); + rmSync(tmp, { force: true, recursive: true }); + mkdirSync(tmp, { recursive: true }); builder.log.minor('Copying assets'); - builder.writeClient(`${out}/client${builder.config.kit.paths.base}`); - builder.writePrerendered(`${out}/prerendered${builder.config.kit.paths.base}`); + const written = [ + ...builder.writeClient(`${out}/client${builder.config.paths.base}`), + ...builder.writePrerendered(`${out}/prerendered${builder.config.paths.base}`) + ]; + + /** @type {string[]} */ + let compressed = []; if (precompress) { builder.log.minor('Compressing assets'); - await Promise.all([ - builder.compress(`${out}/client`), - builder.compress(`${out}/prerendered`) - ]); + compressed = ( + await Promise.all([ + builder.compress(`${out}/client`), + builder.compress(`${out}/prerendered`) + ]) + ).flat(); } - builder.log.minor('Building server'); + const compressed_extensions = new Set(compressed.map((file) => extname(file))); + // a pathname whose extension appears in neither set may be a route segment + // resolving to a compressed `index.html`, so it must keep its `Vary` header + const uncompressed_extensions = new Set( + written.map((file) => extname(file)).filter((ext) => ext && !compressed_extensions.has(ext)) + ); - // Copy the entrypoints into `.svelte-kit/adapter-node/entries`, - // so that node modules are correctly resolved - const entries = `${tmp}/entries`; - builder.copy(files, entries); + builder.log.minor('Building server'); const pkg = JSON.parse(readFileSync('package.json', 'utf8')); const server = builder.getServerDirectory(); + // Copy the prebuilt entrypoints into the build directory so that the + // adapter's own bundled dependencies resolve correctly, then bundle them + // together with the app's server code. Bundling everything in a single + // pass means shared modules (e.g. `SvelteKitError` from `@sveltejs/kit`) + // aren't duplicated. See https://github.com/sveltejs/kit/issues/15755 + const entries = posixify(`${tmp}/entries`); + builder.copy(files, entries); + + const dir_id = `${entries}/dir.js`; + + writeFileSync( + `${server}/manifest.js`, + [ + `export const manifest = ${builder.generateManifest({ relativePath: './' })};`, + `export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});`, + `export const base = ${JSON.stringify(builder.config.paths.base)};`, + `export const uncompressed_extensions = new Set(${JSON.stringify([...uncompressed_extensions])});` + ].join('\n\n') + ); + /** @type {Record} */ const input = { index: `${entries}/index.js`, env: `${entries}/env.js`, - handler: `${entries}/handler.js`, - shims: `${entries}/shims.js` + handler: `${entries}/handler.js` }; - if (builder.hasServerInstrumentationFile?.()) { + if (builder.hasServerInstrumentationFile()) { input['instrumentation.server'] = `${server}/instrumentation.server.js`; } // we bundle the Vite output so that deployments only need // their production dependencies. Anything in devDependencies // will get included in the bundled code - const bundle = await rollup({ + const bundle = await rolldown({ input, external: [ // dependencies could have deep exports, so we need a regex - ...Object.keys(pkg.dependencies || {}).map((d) => new RegExp(`^${d}(\\/.*)?$`)) + ...Object.keys(pkg.dependencies || {}).map((d) => new RegExp(`^${d}(\\/.*)?$`)), + // `@opentelemetry/api` is an optional peer dependency of `@sveltejs/kit`, + // so it's not in `pkg.dependencies` and wouldn't be matched by the regex above. + // It must stay external so that `instrumentation.server.js` and the SvelteKit + // runtime share a single instance — see https://github.com/sveltejs/kit/issues/16288 + /^@opentelemetry\/api(\/.*)?$/ ], + platform: 'node', + resolve: { + conditionNames: ['node'] + }, + experimental: { + nativeMagicString: true + }, plugins: [ { - name: 'adapter-node:alias', + // resolve the app's server and manifest, generated above + name: 'adapter-node-resolve-app', resolveId(id) { if (id === 'SERVER') return `${server}/index.js`; if (id === 'MANIFEST') return `${server}/manifest.js`; } }, - nodeResolve({ - preferBuiltins: true, - exportConditions: ['node'] - }), - // @ts-expect-error https://github.com/rollup/plugins/issues/1329 - replace({ - // only replace tokens in the adapter's own entrypoints, so that - // identifiers like `BASE` in the user's app code or bundled - // dependencies aren't accidentally replaced - include: [`${entries}/**`], - values: { - BASE: JSON.stringify(builder.config.kit.paths.base), - ENV_PREFIX: JSON.stringify(envPrefix), - PRECOMPRESS: JSON.stringify(precompress), - PRERENDERED: `new Set(${JSON.stringify(builder.prerendered.paths)})` - }, - preventAssignment: true - }), - // @ts-expect-error https://github.com/rollup/plugins/issues/1329 - commonjs({ strictRequires: true }), - // @ts-expect-error https://github.com/rollup/plugins/issues/1329 - json() + { + // replace build-time constants in the adapter's own entrypoints + // only, so that identifiers in the app or its dependencies aren't + // accidentally replaced + name: 'adapter-node-replace-constants', + transform: { + filter: { id: new RegExp(escape_regex(entries)) }, + handler(_code, _id, { magicString }) { + if (!magicString) throw new Error('experimental.nativeMagicString is not enabled'); + magicString + .replace(/\bENV_PREFIX\b/g, JSON.stringify(envPrefix)) + .replace(/\bPRECOMPRESS\b/g, JSON.stringify(precompress)) + .replace( + /\bORIGIN\b/g, + JSON.stringify(builder.config.paths.origin) || 'undefined' + ); + return { + code: magicString, + map: magicString.generateMap().toString() + }; + } + } + } ] }); - const server_path_length = server.length + 1; - await bundle.write({ dir: out, format: 'esm', sourcemap: true, - chunkFileNames: 'server/chunks/[name]-[hash].js', - // force the Vite server output to retain their file structure to avoid - // a circular import chain - // see https://github.com/sveltejs/kit/issues/16092 - manualChunks(id) { - if (id.startsWith(server)) { - return id.slice(server_path_length); - } + codeSplitting: { + groups: [ + { + name: 'dir', + test: dir_id + } + ] + }, + chunkFileNames(chunk) { + if (chunk.name === 'dir') return '[name].js'; + return 'server/chunks/[name]-[hash].js'; } }); - if (builder.hasServerInstrumentationFile?.()) { - builder.instrument?.({ + if (builder.hasServerInstrumentationFile()) { + builder.instrument({ entrypoint: `${out}/index.js`, instrumentation: `${out}/instrumentation.server.js`, module: { @@ -143,3 +177,8 @@ export default function (opts = {}) { } }; } + +/** @param {string} str */ +function posixify(str) { + return str.replace(/\\/g, '/'); +} diff --git a/packages/adapter-node/internal.d.ts b/packages/adapter-node/internal.d.ts index e2e22404e9db..8748941c8a6f 100644 --- a/packages/adapter-node/internal.d.ts +++ b/packages/adapter-node/internal.d.ts @@ -1,13 +1,12 @@ declare module 'MANIFEST' { import { SSRManifest } from '@sveltejs/kit'; + + export const base: string; + export const uncompressed_extensions: Set; export const manifest: SSRManifest; + export const prerendered: Set; } declare module 'SERVER' { export { Server } from '@sveltejs/kit'; } - -declare const BASE: string; -declare const ENV_PREFIX: string; -declare const PRECOMPRESS: boolean; -declare const PRERENDERED: Set; diff --git a/packages/adapter-node/package.json b/packages/adapter-node/package.json index 407a1e81457d..f0f4cd3f4a32 100644 --- a/packages/adapter-node/package.json +++ b/packages/adapter-node/package.json @@ -1,6 +1,6 @@ { "name": "@sveltejs/adapter-node", - "version": "5.5.7", + "version": "6.0.0-next.10", "description": "Adapter for SvelteKit apps that generates a standalone Node server", "keywords": [ "adapter", @@ -33,12 +33,13 @@ "ambient.d.ts" ], "scripts": { - "dev": "rollup -cw", - "build": "rollup -c", + "dev": "rolldown -cw", + "build": "rolldown -c", "test": "vitest run", + "test:unit": "vitest run", "check": "tsc", - "lint": "prettier --check .", - "format": "pnpm lint --write", + "lint": "oxfmt --check .", + "format": "oxfmt --write .", "prepublishOnly": "pnpm build" }, "devDependencies": { @@ -47,17 +48,13 @@ "@types/node": "catalog:", "polka": "catalog:", "sirv": "^3.0.2", - "typescript": "catalog:", + "typescript": "catalog:typescript-native", "vitest": "catalog:" }, "dependencies": { - "@rollup/plugin-commonjs": "^29.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^16.0.0", - "@rollup/plugin-replace": "^6.0.3", - "rollup": "^4.59.0" + "rolldown": "^1.2.3" }, "peerDependencies": { - "@sveltejs/kit": "^2.4.0" + "@sveltejs/kit": "^3.0.0-next.0" } } diff --git a/packages/adapter-node/rolldown.config.js b/packages/adapter-node/rolldown.config.js new file mode 100644 index 000000000000..a7fd6d95ea9e --- /dev/null +++ b/packages/adapter-node/rolldown.config.js @@ -0,0 +1,69 @@ +/** @import { Plugin, RolldownOptions } from 'rolldown' */ +import { builtinModules } from 'node:module'; +import { rmSync } from 'node:fs'; +import { join } from 'node:path'; + +/** + * @param {string} filepath + * @returns {Plugin} + */ +function clearOutput(filepath) { + return { + name: 'clear-output', + buildStart: { + order: 'pre', + sequential: true, + handler() { + rmSync(filepath, { recursive: true, force: true }); + } + } + }; +} + +/** + * @returns {Plugin} + */ +function prefixBuiltinModules() { + return { + name: 'prefix-built-in-modules', + resolveId(source) { + if (builtinModules.includes(source)) { + return { id: 'node:' + source, external: true }; + } + } + }; +} + +const dir_id = join(import.meta.dirname, 'src', 'dir.js'); + +/** @type {RolldownOptions} */ +export default { + input: { + index: 'src/index.js', + handler: 'src/handler.js', + env: 'src/env.js' + }, + output: { + dir: 'files', + format: 'esm', + hoistTransitiveImports: false, + chunkFileNames(chunk) { + if (chunk.name === 'dir') return '[name].js'; + return 'chunks/[name].js'; + }, + codeSplitting: { + groups: [ + { + name: 'dir', + test: dir_id + } + ] + } + }, + plugins: [clearOutput('files'), prefixBuiltinModules()], + // `MANIFEST` and `SERVER` are resolved at adapt time, and `@sveltejs/kit/node` + // is kept external so that it gets bundled _alongside_ the app's server code + // (rather than duplicated), see https://github.com/sveltejs/kit/issues/15755 + external: ['MANIFEST', 'SERVER', '@sveltejs/kit/node'], + platform: 'node' +}; diff --git a/packages/adapter-node/rollup.config.js b/packages/adapter-node/rollup.config.js deleted file mode 100644 index e501635c7186..000000000000 --- a/packages/adapter-node/rollup.config.js +++ /dev/null @@ -1,69 +0,0 @@ -/** @import { Plugin, RollupOptions } from 'rollup' */ -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import json from '@rollup/plugin-json'; -import { builtinModules } from 'node:module'; -import { rmSync } from 'node:fs'; - -/** - * @param {string} filepath - * @returns {Plugin} - */ -function clearOutput(filepath) { - return { - name: 'clear-output', - buildStart: { - order: 'pre', - sequential: true, - handler() { - rmSync(filepath, { recursive: true, force: true }); - } - } - }; -} - -/** - * @returns {Plugin} - */ -function prefixBuiltinModules() { - return { - name: 'prefix-built-in-modules', - resolveId(source) { - if (builtinModules.includes(source)) { - return { id: 'node:' + source, external: true }; - } - } - }; -} - -/** @type {RollupOptions} */ -export default { - input: { - index: 'src/index.js', - env: 'src/env.js', - handler: 'src/handler.js', - shims: 'src/shims.js', - utils: 'utils.js' - }, - output: { - dir: 'files', - format: 'esm', - hoistTransitiveImports: false, - chunkFileNames: 'chunks/[name].js', - // prevent the handler code from becoming a shared chunk when both - // handler.js and index.js are input entries - manualChunks(id) { - if (id.includes('node_modules')) return 'vendor'; - } - }, - plugins: [ - clearOutput('files'), - nodeResolve({ preferBuiltins: true }), - // @ts-expect-error https://github.com/rollup/plugins/issues/1329 - commonjs(), - // @ts-expect-error https://github.com/rollup/plugins/issues/1329 - json(), - prefixBuiltinModules() - ], - external: ['MANIFEST', 'SERVER', '@sveltejs/kit/node', '@sveltejs/kit/node/polyfills'] -}; diff --git a/packages/adapter-node/src/dir.js b/packages/adapter-node/src/dir.js new file mode 100644 index 000000000000..580da43e146f --- /dev/null +++ b/packages/adapter-node/src/dir.js @@ -0,0 +1,6 @@ +import { dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +// this needs to end up in the root of the output directory +// otherwise, it would cause incorrect paths to be constructed +export const dir = dirname(fileURLToPath(import.meta.url)); diff --git a/packages/adapter-node/src/env.js b/packages/adapter-node/src/env.js index 7b57d9211166..db6bb13422d3 100644 --- a/packages/adapter-node/src/env.js +++ b/packages/adapter-node/src/env.js @@ -1,15 +1,9 @@ -import path from 'node:path'; import process from 'node:process'; -import { fileURLToPath } from 'node:url'; - -// since env.js is an entrypoint, `dir` will point to the output directory -export const dir = path.dirname(fileURLToPath(import.meta.url)); const expected = new Set([ 'SOCKET_PATH', 'HOST', 'PORT', - 'ORIGIN', 'XFF_DEPTH', 'ADDRESS_HEADER', 'PROTOCOL_HEADER', diff --git a/packages/adapter-node/src/handler.js b/packages/adapter-node/src/handler.js index 4ee40941f417..f79534e3dcad 100644 --- a/packages/adapter-node/src/handler.js +++ b/packages/adapter-node/src/handler.js @@ -1,21 +1,18 @@ -import './shims.js'; import fs from 'node:fs'; -import path from 'node:path'; +import path, { extname } from 'node:path'; import process from 'node:process'; import sirv from 'sirv'; import { parse as polka_url_parser } from '@polka/url'; import { getRequest, setResponse, createReadableStream } from '@sveltejs/kit/node'; import { Server } from 'SERVER'; -import { manifest } from 'MANIFEST'; -import { dir, env, env_prefix } from './env.js'; -import { parse_as_bytes, parse_origin } from '../utils.js'; - -const prerendered = PRERENDERED; +import { manifest, prerendered, base, uncompressed_extensions } from 'MANIFEST'; +import { dir } from './dir.js'; +import { env, env_prefix } from './env.js'; +import { parse_as_bytes } from './utils.js'; const server = new Server(manifest); -// parse_origin validates ORIGIN and throws descriptive errors for invalid values -const origin = parse_origin(env('ORIGIN', undefined)); +const origin = ORIGIN; const xff_depth = parseInt(env('XFF_DEPTH', '1')); const address_header = env('ADDRESS_HEADER', '').toLowerCase(); @@ -31,10 +28,10 @@ if (isNaN(body_size_limit)) { ); } -const asset_dir = `${dir}/client${BASE}`; +const asset_dir = `${dir}/client${base}`; await server.init({ - env: /** @type {Record} */ (process.env), + env: process.env, read: (file) => createReadableStream(`${asset_dir}/${file}`) }); @@ -48,21 +45,43 @@ function serve(path, client = false) { etag: true, gzip: PRECOMPRESS, brotli: PRECOMPRESS, - setHeaders: client - ? (res, pathname) => { - // only apply to build directory, not e.g. version.json - if ( - pathname.startsWith(`/${manifest.appPath}/immutable/`) && - res.statusCode === 200 - ) { - res.setHeader('cache-control', 'public,max-age=31536000,immutable'); - } - } - : undefined + setHeaders: (res, pathname) => { + // `sirv` sets `Vary` from its options rather than from the file it resolved + if (PRECOMPRESS && uncompressed_extensions.has(extname(pathname))) { + res.removeHeader('vary'); + } + + // `sirv` uses its own bundled `mrmime`, which the manifest's added types never reach + let type = manifest.mimeTypes[pathname.slice(pathname.lastIndexOf('.'))]; + if (type === 'text/html') type += ';charset=utf-8'; + if (type) res.setHeader('content-type', type); + + // only apply to build directory, not e.g. version.json + if ( + client && + pathname.startsWith(`/${manifest.appPath}/immutable/`) && + res.statusCode === 200 + ) { + res.setHeader('cache-control', 'public,max-age=31536000,immutable'); + } + } }) : undefined; } +/** + * Relative reference from `from` to `to`, which must differ only by a trailing slash. + * Keep in sync with the copy in `packages/kit/src/utils/url.js` + * @param {string} from + * @param {string} to + * @returns {string} + */ +function relative_pathname(from, to) { + const segment = to.replace(/\/$/, '').split('/').at(-1); + + return from.endsWith('/') ? `../${segment}` : `${segment}/`; +} + // required because the static file server ignores trailing slashes /** @returns {import('polka').Middleware} */ function serve_prerendered() { @@ -82,9 +101,9 @@ function serve_prerendered() { } // remove or add trailing slash as appropriate - let location = pathname.at(-1) === '/' ? pathname.slice(0, -1) : pathname + '/'; - if (prerendered.has(location)) { - if (query) location += search; + const inverted = pathname.at(-1) === '/' ? pathname.slice(0, -1) : pathname + '/'; + if (prerendered.has(inverted)) { + const location = relative_pathname(pathname, inverted) + (query ? search : ''); res.writeHead(308, { location }).end(); } else { void next(); @@ -96,11 +115,26 @@ function serve_prerendered() { const ssr = async (req, res) => { /** @type {Request} */ let request; + let request_origin = origin; + + if (!request_origin) { + try { + request_origin = get_origin(req.headers); + } catch (error) { + console.error( + `Could not determine request origin: ${error instanceof Error ? error.message : String(error)}` + ); + res.statusCode = 400; + res.end('Bad Request'); + return; + } + } try { - request = await getRequest({ - base: origin || get_origin(req.headers), + request = getRequest({ + base: request_origin, request: req, + response: res, bodySizeLimit: body_size_limit }); } catch { @@ -162,7 +196,7 @@ const ssr = async (req, res) => { response.headers.set('x-accel-buffering', 'no'); } - await setResponse(res, response); + setResponse(res, response); }; /** @param {import('polka').Middleware[]} handlers */ diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js index 879060b1bbb8..b72d6b0d2965 100644 --- a/packages/adapter-node/src/index.js +++ b/packages/adapter-node/src/index.js @@ -5,7 +5,7 @@ import { handler } from './handler.js'; import { env, timeout_env } from './env.js'; import polka from 'polka'; import { rm } from 'node:fs/promises'; -import { format_listening_address } from '../utils.js'; +import { format_listening_address } from './utils.js'; export const path = env('SOCKET_PATH', false); export const host = env('HOST', '0.0.0.0'); diff --git a/packages/adapter-node/src/shims.js b/packages/adapter-node/src/shims.js deleted file mode 100644 index 2490311daa1e..000000000000 --- a/packages/adapter-node/src/shims.js +++ /dev/null @@ -1,2 +0,0 @@ -import { installPolyfills } from '@sveltejs/kit/node/polyfills'; -installPolyfills(); diff --git a/packages/adapter-node/utils.js b/packages/adapter-node/src/utils.js similarity index 53% rename from packages/adapter-node/utils.js rename to packages/adapter-node/src/utils.js index 38c543ef39c8..95b78986cc4a 100644 --- a/packages/adapter-node/utils.js +++ b/packages/adapter-node/src/utils.js @@ -16,43 +16,6 @@ export function parse_as_bytes(value) { return Number(multiplier != 1 ? value.substring(0, value.length - 1) : value) * multiplier; } -/** - * Parses and validates an origin URL. - * - * @param {string | undefined} value - Origin URL with http:// or https:// protocol - * @returns {string | undefined} The validated origin, or undefined if value is undefined - * @throws {Error} If value is provided but invalid - */ -export function parse_origin(value) { - if (value === undefined) { - return undefined; - } - - const trimmed = value.trim(); - - let url; - try { - url = new URL(trimmed); - } catch (error) { - throw new Error( - `Invalid ORIGIN: '${trimmed}'. ` + - `ORIGIN must be a valid URL with http:// or https:// protocol. ` + - `For example: 'http://localhost:3000' or 'https://my.site'`, - { cause: error } - ); - } - - if (url.protocol !== 'http:' && url.protocol !== 'https:') { - throw new Error( - `Invalid ORIGIN: '${trimmed}'. ` + - `Only http:// and https:// protocols are supported. ` + - `Received protocol: ${url.protocol}` - ); - } - - return url.origin; -} - /** * Formats the address the server is listening on. * diff --git a/packages/adapter-node/src/utils.spec.ts b/packages/adapter-node/src/utils.spec.ts new file mode 100644 index 000000000000..6af2f2612f86 --- /dev/null +++ b/packages/adapter-node/src/utils.spec.ts @@ -0,0 +1,50 @@ +import { expect, test, describe } from 'vitest'; +import { format_listening_address, parse_as_bytes } from './utils.js'; + +describe('parse_as_bytes', () => { + test.each([ + ['200', 200], + ['512K', 512 * 1024], + ['200M', 200 * 1024 * 1024], + ['1G', 1024 * 1024 * 1024], + ['Infinity', Infinity], + ['asdf', NaN] + ] as const)('parses correctly', (input, expected) => { + const actual = parse_as_bytes(input); + expect(actual, `Testing input '${input}'`).toBe(expected); + }); +}); + +describe('format_listening_address', () => { + test('uses the actual listening port assigned by the server', () => { + expect( + format_listening_address(false, '0.0.0.0', '0', { + address: '0.0.0.0', + family: 'IPv4', + port: 43521 + }) + ).toBe('http://0.0.0.0:43521'); + }); + + test('formats IPv6 addresses as valid URLs', () => { + expect( + format_listening_address(false, '::1', '3000', { + address: '::1', + family: 'IPv6', + port: 3000 + }) + ).toBe('http://[::1]:3000'); + }); + + test('falls back to configured host and port when the address is unavailable', () => { + expect(format_listening_address(false, 'localhost', '3000', null)).toBe( + 'http://localhost:3000' + ); + }); + + test('returns the socket path unchanged', () => { + expect(format_listening_address('/tmp/sveltekit.sock', '0.0.0.0', '3000', null)).toBe( + '/tmp/sveltekit.sock' + ); + }); +}); diff --git a/packages/adapter-node/test/apps/basic/package.json b/packages/adapter-node/test/apps/basic/package.json index 5686e13c1011..86b41481b8eb 100644 --- a/packages/adapter-node/test/apps/basic/package.json +++ b/packages/adapter-node/test/apps/basic/package.json @@ -4,9 +4,8 @@ "private": true, "scripts": { "dev": "vite dev", - "build": "vite build", + "build": "MY_CUSTOM_PORT=5173 vite build", "preview": "MY_CUSTOM_PORT=5173 node build", - "prepare": "svelte-kit sync || echo ''", "test:platform": "playwright test" }, "devDependencies": { diff --git a/packages/adapter-node/test/apps/basic/src/env.js b/packages/adapter-node/test/apps/basic/src/env.js new file mode 100644 index 000000000000..7080cd2e1e41 --- /dev/null +++ b/packages/adapter-node/test/apps/basic/src/env.js @@ -0,0 +1,5 @@ +import { defineEnvVars } from '@sveltejs/kit/env'; + +export const variables = defineEnvVars({ + MY_CUSTOM_PORT: {} +}); diff --git a/packages/adapter-node/test/apps/basic/src/hooks.server.js b/packages/adapter-node/test/apps/basic/src/hooks.server.js index fda64966449e..008bd4bd68a4 100644 --- a/packages/adapter-node/test/apps/basic/src/hooks.server.js +++ b/packages/adapter-node/test/apps/basic/src/hooks.server.js @@ -1,9 +1,9 @@ -// importing $env in the server hook can cause a cyclical import that crashes the app start up +// importing $app/env/* in the server hook can cause a cyclical import that crashes the app start up // see https://github.com/sveltejs/kit/issues/16092 -import { env } from '$env/dynamic/private'; +import { MY_CUSTOM_PORT } from '$app/env/private'; -/** @type {import('@sveltejs/kit').Handle} */ +/** @type {import('@sveltejs/kit/hooks').Handle} */ export function handle({ event, resolve }) { - if (!env) throw new Error('env should be defined'); + if (!MY_CUSTOM_PORT) throw new Error('MY_CUSTOM_PORT should be defined'); return resolve(event); } diff --git a/packages/adapter-node/test/apps/basic/src/routes/prerendered.ico/+server.js b/packages/adapter-node/test/apps/basic/src/routes/prerendered.ico/+server.js new file mode 100644 index 000000000000..ea5bb293b791 --- /dev/null +++ b/packages/adapter-node/test/apps/basic/src/routes/prerendered.ico/+server.js @@ -0,0 +1,7 @@ +export const prerender = true; + +export function GET() { + return new Response(new Uint8Array([0, 0, 1, 0]), { + headers: { 'content-type': 'image/x-icon' } + }); +} diff --git a/packages/adapter-node/test/apps/basic/static/data.json b/packages/adapter-node/test/apps/basic/static/data.json new file mode 100644 index 000000000000..5de601c012b8 --- /dev/null +++ b/packages/adapter-node/test/apps/basic/static/data.json @@ -0,0 +1 @@ +{ "compressible": true } diff --git a/packages/adapter-node/test/apps/basic/static/page.html b/packages/adapter-node/test/apps/basic/static/page.html new file mode 100644 index 000000000000..3ec9ee860d0f --- /dev/null +++ b/packages/adapter-node/test/apps/basic/static/page.html @@ -0,0 +1,2 @@ + +

hi

diff --git a/packages/adapter-node/test/apps/basic/static/sub/index.html b/packages/adapter-node/test/apps/basic/static/sub/index.html new file mode 100644 index 000000000000..838a7c81782a --- /dev/null +++ b/packages/adapter-node/test/apps/basic/static/sub/index.html @@ -0,0 +1,2 @@ + +

index

diff --git a/packages/adapter-node/test/apps/basic/static/test.ico b/packages/adapter-node/test/apps/basic/static/test.ico new file mode 100644 index 000000000000..5b94bb90acc5 Binary files /dev/null and b/packages/adapter-node/test/apps/basic/static/test.ico differ diff --git a/packages/adapter-node/test/apps/basic/static/v1.0/index.html b/packages/adapter-node/test/apps/basic/static/v1.0/index.html new file mode 100644 index 000000000000..88833dd9fc63 --- /dev/null +++ b/packages/adapter-node/test/apps/basic/static/v1.0/index.html @@ -0,0 +1,2 @@ + +

dotted segment

diff --git a/packages/adapter-node/test/apps/basic/test/test.js b/packages/adapter-node/test/apps/basic/test/test.js index a3bae54d9c1f..94450a6b8d35 100644 --- a/packages/adapter-node/test/apps/basic/test/test.js +++ b/packages/adapter-node/test/apps/basic/test/test.js @@ -22,3 +22,52 @@ test('does not set X-Accel-Buffering header on other responses', async ({ reques const response = await request.get('/'); expect(response.headers()['x-accel-buffering']).toBeUndefined(); }); + +test('sets Vary on assets that were precompressed', async ({ request }) => { + const response = await request.get('/data.json'); + expect(response.status()).toBe(200); + expect(response.headers()['vary']).toBe('Accept-Encoding'); +}); + +test('does not set Vary on assets that were not precompressed', async ({ request }) => { + const response = await request.get('/test.ico'); + expect(response.status()).toBe(200); + expect(response.headers()['vary']).toBeUndefined(); +}); + +// an extensionless pathname can still resolve to a precompressed `index.html` +test('sets Vary on assets reached without an extension', async ({ request }) => { + const response = await request.get('/sub/'); + expect(response.status()).toBe(200); + expect(response.headers()['content-type']).toBe('text/html;charset=utf-8'); + expect(response.headers()['vary']).toBe('Accept-Encoding'); +}); + +// a dot in the final path segment looks like an extension but isn't one +test('sets Vary on assets reached via a dotted path segment', async ({ request }) => { + const response = await request.get('/v1.0/'); + expect(response.status()).toBe(200); + expect(response.headers()['content-type']).toBe('text/html;charset=utf-8'); + expect(response.headers()['vary']).toBe('Accept-Encoding'); +}); + +test('serves static files with the Content-Type from the manifest', async ({ request }) => { + // https://github.com/sveltejs/kit/issues/13753 + const response = await request.get('/test.ico'); + expect(response.status()).toBe(200); + expect(response.headers()['content-type']).toBe('image/x-icon'); +}); + +test('serves prerendered endpoints with the Content-Type from the manifest', async ({ + request +}) => { + const response = await request.get('/prerendered.ico'); + expect(response.status()).toBe(200); + expect(response.headers()['content-type']).toBe('image/x-icon'); +}); + +test('serves static HTML with a charset', async ({ request }) => { + const response = await request.get('/page.html'); + expect(response.status()).toBe(200); + expect(response.headers()['content-type']).toBe('text/html;charset=utf-8'); +}); diff --git a/packages/adapter-node/test/apps/basic/tsconfig.json b/packages/adapter-node/test/apps/basic/tsconfig.json index c2db702f9bb1..8e210f5f6f5d 100644 --- a/packages/adapter-node/test/apps/basic/tsconfig.json +++ b/packages/adapter-node/test/apps/basic/tsconfig.json @@ -1,13 +1,4 @@ { - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "moduleResolution": "bundler" - }, - "extends": "./.svelte-kit/tsconfig.json" + "extends": "$app/tsconfig", + "include": ["src", "test", "*.js"] } diff --git a/packages/adapter-node/test/apps/basic/vite.config.js b/packages/adapter-node/test/apps/basic/vite.config.js index ab8fc581e9d4..71f440a54514 100644 --- a/packages/adapter-node/test/apps/basic/vite.config.js +++ b/packages/adapter-node/test/apps/basic/vite.config.js @@ -10,12 +10,7 @@ const config = { sveltekit({ adapter: adapter({ envPrefix: 'MY_CUSTOM_' - }), - typescript: { - config(config) { - config.include.push('../playwright.config.js'); - } - } + }) }) ] }; diff --git a/packages/adapter-node/test/utils.js b/packages/adapter-node/test/utils.js index c553b42b7534..d4830c2f1a13 100644 --- a/packages/adapter-node/test/utils.js +++ b/packages/adapter-node/test/utils.js @@ -21,7 +21,7 @@ export const config = { ...devices['Desktop Chrome'], screenshot: 'only-on-failure', trace: 'retain-on-failure', - channel: 'chromium' + channel: 'chrome' }, workers: process.env.CI ? 2 : number_from_env('KIT_E2E_WORKERS', undefined), reporter: 'list', diff --git a/packages/adapter-node/tsconfig.json b/packages/adapter-node/tsconfig.json index 12ae3813e896..8a885a923b9d 100644 --- a/packages/adapter-node/tsconfig.json +++ b/packages/adapter-node/tsconfig.json @@ -13,15 +13,14 @@ }, "include": [ "index.js", + "rolldown.config.js", + "vitest.config.js", "src/**/*.js", "src/**/*.ts", "test/utils.js", "tests/**/*.js", "tests/**/*.ts", - "utils.js", - "./internal.d.ts", - "./*.spec.ts", - "rollup.config.js" + "./internal.d.ts" ], "exclude": ["smoke.spec_disabled.js"] } diff --git a/packages/adapter-node/utils.spec.ts b/packages/adapter-node/utils.spec.ts deleted file mode 100644 index 09938918d871..000000000000 --- a/packages/adapter-node/utils.spec.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { expect, test, describe } from 'vitest'; -import { format_listening_address, parse_as_bytes, parse_origin } from './utils.js'; - -describe('parse_as_bytes', () => { - test.each([ - ['200', 200], - ['512K', 512 * 1024], - ['200M', 200 * 1024 * 1024], - ['1G', 1024 * 1024 * 1024], - ['Infinity', Infinity], - ['asdf', NaN] - ] as const)('parses correctly', (input, expected) => { - const actual = parse_as_bytes(input); - expect(actual, `Testing input '${input}'`).toBe(expected); - }); -}); - -describe('parse_origin', () => { - test.each([ - ['http://localhost:3000', 'http://localhost:3000'], - ['https://example.com', 'https://example.com'], - ['http://192.168.1.1:8080', 'http://192.168.1.1:8080'], - ['https://my-site.com', 'https://my-site.com'], - ['http://localhost', 'http://localhost'], - // Default ports are normalized by URL.origin per WHATWG URL standard - ['https://example.com:443', 'https://example.com'], - ['http://example.com:80', 'http://example.com'], - [undefined, undefined] - ] as const)('normalizes %s to %s', (input, expected) => { - expect(parse_origin(input)).toBe(expected); - }); - - test.each([ - ['http://localhost:3000/path', 'http://localhost:3000'], - ['http://localhost:3000?query=1', 'http://localhost:3000'], - ['http://localhost:3000#hash', 'http://localhost:3000'], - ['https://example.com/path/to/page', 'https://example.com'], - ['https://example.com:443/path?query=1#hash', 'https://example.com'] - ] as const)('strips path/query/hash from %s to get %s', (input, expected) => { - expect(parse_origin(input)).toBe(expected); - }); - - test.each(['localhost:3000', 'example.com', '', ' ', 'ftp://localhost:3000'] as const)( - 'throws error for invalid origin: %s', - (input) => { - expect(() => parse_origin(input)).toThrow('Invalid ORIGIN'); - } - ); -}); - -describe('format_listening_address', () => { - test('uses the actual listening port assigned by the server', () => { - expect( - format_listening_address(false, '0.0.0.0', '0', { - address: '0.0.0.0', - family: 'IPv4', - port: 43521 - }) - ).toBe('http://0.0.0.0:43521'); - }); - - test('formats IPv6 addresses as valid URLs', () => { - expect( - format_listening_address(false, '::1', '3000', { - address: '::1', - family: 'IPv6', - port: 3000 - }) - ).toBe('http://[::1]:3000'); - }); - - test('falls back to configured host and port when the address is unavailable', () => { - expect(format_listening_address(false, 'localhost', '3000', null)).toBe( - 'http://localhost:3000' - ); - }); - - test('returns the socket path unchanged', () => { - expect(format_listening_address('/tmp/sveltekit.sock', '0.0.0.0', '3000', null)).toBe( - '/tmp/sveltekit.sock' - ); - }); -}); diff --git a/packages/adapter-node/vitest.config.js b/packages/adapter-node/vitest.config.js new file mode 100644 index 000000000000..a15b3a470a6d --- /dev/null +++ b/packages/adapter-node/vitest.config.js @@ -0,0 +1,5 @@ +// we need this file to prevent Vitest from resolving a Vitest config from another directory + +import { defineConfig } from 'vitest/config'; + +export default defineConfig({}); diff --git a/packages/adapter-static/CHANGELOG.md b/packages/adapter-static/CHANGELOG.md index 9202756132d2..e7e953f27f5b 100644 --- a/packages/adapter-static/CHANGELOG.md +++ b/packages/adapter-static/CHANGELOG.md @@ -1,5 +1,59 @@ # @sveltejs/adapter-static +## 4.0.0-next.4 + +### Major Changes + +- breaking: require SvelteKit 3 + +### Patch Changes + +- chore: use `node:fs` instead of deprecated `builder.rimraf` and `builder.mkdirp` ([#16286](https://github.com/sveltejs/kit/pull/16286)) + +- fix: avoid caching immutable asset 404s when configuring for Vercel + +- fix: match adapter-vercel's prerendered redirect handling when deploying to Vercel + +- fix: allow prerelease versions of SvelteKit 3 to satisfy the peer dependency range +- Updated dependencies [[`bbd1e2f`](https://github.com/sveltejs/kit/commit/bbd1e2fcc9af58edb9846393b081be61df4789d5), [`e21d27b`](https://github.com/sveltejs/kit/commit/e21d27ba490c89da98dbad24fca364d0163137c4), [`6044d05`](https://github.com/sveltejs/kit/commit/6044d055031bc17deedc069dc4016565364bd4f4), [`7fb611f`](https://github.com/sveltejs/kit/commit/7fb611fb604e4d74cd889f5fe21ccd255f876382), [`9f06b3a`](https://github.com/sveltejs/kit/commit/9f06b3ac78be30d27b07fc00ea40bda493c573aa), [`8763327`](https://github.com/sveltejs/kit/commit/87633271ce40ac0839be9468598e965bde3cad51), [`7dc2379`](https://github.com/sveltejs/kit/commit/7dc23798cde97b5616049ba8d443a07e8c558bdc), [`a70c3eb`](https://github.com/sveltejs/kit/commit/a70c3eb31fe1e28f1532c84ec569ec7099da3344), [`89f49c7`](https://github.com/sveltejs/kit/commit/89f49c70b7fb4883cf704d2fb57d2d8595790ad2), [`f36b3a4`](https://github.com/sveltejs/kit/commit/f36b3a4cd307bd8c89e03fa191becc175e7b3077), [`1144a63`](https://github.com/sveltejs/kit/commit/1144a63d318c3c3255d06b374bb7a656a469fabf), [`9f06b3a`](https://github.com/sveltejs/kit/commit/9f06b3ac78be30d27b07fc00ea40bda493c573aa)]: + - @sveltejs/kit@3.0.0-next.15 + +## 4.0.0-next.3 + +### Patch Changes + +- fix: avoid caching immutable asset 404s when configuring for Vercel ([#16626](https://github.com/sveltejs/kit/pull/16626)) +- Updated dependencies [[`c3f58bb`](https://github.com/sveltejs/kit/commit/c3f58bb0eb6bde790897f1e8b89019bc39a062e5), [`b655dc7`](https://github.com/sveltejs/kit/commit/b655dc7cf043ff01d53e494e9744e548bb3ad9fd), [`a70cc4f`](https://github.com/sveltejs/kit/commit/a70cc4f2ca24e7099c898456085ffe18d191657e), [`b325afb`](https://github.com/sveltejs/kit/commit/b325afb509b54445f678348e88ea2bf0152f4cec), [`e76b3d7`](https://github.com/sveltejs/kit/commit/e76b3d778bacb429fdb40a41b13ff04b54d97c1f), [`58f47eb`](https://github.com/sveltejs/kit/commit/58f47eb94016c5405829667bbc873e027c7b363c), [`fe9d8d9`](https://github.com/sveltejs/kit/commit/fe9d8d939b6d1a07b2957104fda6a1123f5eebd3), [`0c7bbd7`](https://github.com/sveltejs/kit/commit/0c7bbd7bd4a346c560e7cbf9b39ba8bda7c718f0), [`08d7e2a`](https://github.com/sveltejs/kit/commit/08d7e2a1b327d4e68c217f6e7d6b305732c82b1b), [`f70f679`](https://github.com/sveltejs/kit/commit/f70f679143d4330b6d3b7d0e690145b5487df97f), [`0624b26`](https://github.com/sveltejs/kit/commit/0624b2678efd41223f70972f7bff2d81d4a5c7f9), [`0624b26`](https://github.com/sveltejs/kit/commit/0624b2678efd41223f70972f7bff2d81d4a5c7f9), [`f4222ad`](https://github.com/sveltejs/kit/commit/f4222adc504a7041b50325424a3eb86af02961ed), [`089628b`](https://github.com/sveltejs/kit/commit/089628b0d9637578085ad61b5786cd1c4476e399), [`d8617b3`](https://github.com/sveltejs/kit/commit/d8617b3f2f08144f6acfc7c636b89d62b982a33d), [`c7369a1`](https://github.com/sveltejs/kit/commit/c7369a148169dfd51af150c461ff266977b6befd), [`5e5e592`](https://github.com/sveltejs/kit/commit/5e5e592f093d05c6ea30a41a0ebf543829069c5f), [`60057c1`](https://github.com/sveltejs/kit/commit/60057c1d2a1e6429e5f315d587ebc01baef6dc94), [`f70f679`](https://github.com/sveltejs/kit/commit/f70f679143d4330b6d3b7d0e690145b5487df97f), [`38f1528`](https://github.com/sveltejs/kit/commit/38f15289e249942275eab1281516702de08b7a30), [`40092f3`](https://github.com/sveltejs/kit/commit/40092f34aa063d033fc7a4d1d6fb1c10abd8cbf6), [`ff4247e`](https://github.com/sveltejs/kit/commit/ff4247e2376862207dd9df7317f39d8bb8dd9ee4), [`79a5de5`](https://github.com/sveltejs/kit/commit/79a5de581fc1bb708879678b8295706221ba0f0f), [`a70cc4f`](https://github.com/sveltejs/kit/commit/a70cc4f2ca24e7099c898456085ffe18d191657e)]: + - @sveltejs/kit@3.0.0-next.14 + +## 4.0.0-next.2 + +### Patch Changes + +- fix: match adapter-vercel's prerendered redirect handling when deploying to Vercel ([#16497](https://github.com/sveltejs/kit/pull/16497)) +- Updated dependencies [[`6942ddb`](https://github.com/sveltejs/kit/commit/6942ddb68b43b5451337a8b420ce05a7a5de9919), [`68137d4`](https://github.com/sveltejs/kit/commit/68137d48cbf921264c6325ffa940165563c0b25a), [`15dc603`](https://github.com/sveltejs/kit/commit/15dc603f5027ee21b261c0c93f7decbf8f9498d8), [`a4f1521`](https://github.com/sveltejs/kit/commit/a4f15213c46f34aac88d05eb9af0905a5f776353), [`629b45d`](https://github.com/sveltejs/kit/commit/629b45d9e68846d862c10006fdabcc1526849811), [`7a7833a`](https://github.com/sveltejs/kit/commit/7a7833af90c0fa70c926cc84d61ca03f9db97bd2), [`e6591a0`](https://github.com/sveltejs/kit/commit/e6591a02743a2b324aa30cd463620450264fabae), [`7a7833a`](https://github.com/sveltejs/kit/commit/7a7833af90c0fa70c926cc84d61ca03f9db97bd2), [`b196a39`](https://github.com/sveltejs/kit/commit/b196a3965dd892a612cc6eae4868e450cc325736), [`66bf343`](https://github.com/sveltejs/kit/commit/66bf343453f8babf719193629a36f084d7bddf75), [`4bde76b`](https://github.com/sveltejs/kit/commit/4bde76b8a7ffa6885878d74eab3e542f1bda9c80), [`d9f05cb`](https://github.com/sveltejs/kit/commit/d9f05cb8efd8682bf91635f517f402849570cb20), [`40efca1`](https://github.com/sveltejs/kit/commit/40efca1b4afd6f0b6329bed652025b99edc8978c), [`8ef5548`](https://github.com/sveltejs/kit/commit/8ef5548f3e63ebb8371099a91acc182f7d094557), [`9efb8f2`](https://github.com/sveltejs/kit/commit/9efb8f2440328a536ebbb70a9e36ce4e30815e8a), [`6a670dc`](https://github.com/sveltejs/kit/commit/6a670dc778561e1fc504c2ccce864a46c2027dcf), [`3416dcc`](https://github.com/sveltejs/kit/commit/3416dccdceddf70ad229b5a1963d0735a7146c01), [`6850fa5`](https://github.com/sveltejs/kit/commit/6850fa56841afc8b1178fe16b6e61ffdfc854ab2), [`e6591a0`](https://github.com/sveltejs/kit/commit/e6591a02743a2b324aa30cd463620450264fabae), [`688bead`](https://github.com/sveltejs/kit/commit/688bead796f60db2384174e4f229c1d558a9ef00), [`b596610`](https://github.com/sveltejs/kit/commit/b596610a0c2e31e7d9adbdfb8d1354a165b5fe23), [`0f26c13`](https://github.com/sveltejs/kit/commit/0f26c133d3c5c19ed375f097b1eb27f6ce1928cd), [`110a3db`](https://github.com/sveltejs/kit/commit/110a3dbdba5f668d3e51b5e9f0d64be5a9ae5b39), [`665d467`](https://github.com/sveltejs/kit/commit/665d46720d26f545cf5ff52c86e44c9b2aa2521b), [`9c8c60c`](https://github.com/sveltejs/kit/commit/9c8c60c26c44e0705091de30a9a485b39d593def)]: + - @sveltejs/kit@3.0.0-next.13 + +## 4.0.0-next.1 + +### Patch Changes + +- fix: allow prerelease versions of SvelteKit 3 to satisfy the peer dependency range ([#16286](https://github.com/sveltejs/kit/pull/16286)) +- Updated dependencies [[`737d119`](https://github.com/sveltejs/kit/commit/737d119730ec163832f838b3813c9cbb75dd799f), [`fa78efb`](https://github.com/sveltejs/kit/commit/fa78efb626031497eecc3a88fa7b6f6da36eca0f), [`07c207e`](https://github.com/sveltejs/kit/commit/07c207ed919570d4ed4954f751056b4f838abdea), [`a47071b`](https://github.com/sveltejs/kit/commit/a47071bb6581fe5f6df4cc47b80e9f0f594624e5), [`14d7d5a`](https://github.com/sveltejs/kit/commit/14d7d5abd9cd8462e4c79ed6a5c10bc43258e97a), [`5c38e51`](https://github.com/sveltejs/kit/commit/5c38e515db7fbb92e5ae01db84b4f0040a02f187), [`0702baa`](https://github.com/sveltejs/kit/commit/0702baa58b2a4600e83b55ef4a85e9cbe3391a4f), [`e1938c6`](https://github.com/sveltejs/kit/commit/e1938c62348b8ccc83e186b1acad76a0a10cc9ea), [`8293144`](https://github.com/sveltejs/kit/commit/8293144b6fbfff9f97cdb1d2224a5eaa04c84fc4), [`f76d7d9`](https://github.com/sveltejs/kit/commit/f76d7d91e18a0afd1f6798c095eb8c170c108cff), [`ab5c253`](https://github.com/sveltejs/kit/commit/ab5c253efb2546ead74db4c8c9b4c7639d97e0ed), [`b557b1b`](https://github.com/sveltejs/kit/commit/b557b1ba76903e1fb0fc3bb87bddc5420332137a), [`4a513e2`](https://github.com/sveltejs/kit/commit/4a513e27ab2ad44dae0c6a4a6c8deda840008530)]: + - @sveltejs/kit@3.0.0-next.8 + +## 4.0.0-next.0 +### Major Changes + + +- breaking: require SvelteKit 3 ([#15506](https://github.com/sveltejs/kit/pull/15506)) + + +### Patch Changes + +- Updated dependencies [[`fa335bd`](https://github.com/sveltejs/kit/commit/fa335bd8af3a982d68c4d34b813b68887dc7cce8), [`3031d89`](https://github.com/sveltejs/kit/commit/3031d89087d49297e89b6566108a1a1105c38475), [`cb9d416`](https://github.com/sveltejs/kit/commit/cb9d416d05266b6237f2856fb94df5b8a9b04279), [`caf3a18`](https://github.com/sveltejs/kit/commit/caf3a18de6581ab7d454d6fa8ad37ebdbdef95e6), [`4777827`](https://github.com/sveltejs/kit/commit/4777827246727045cf0209399751377564e7951d), [`a2792e2`](https://github.com/sveltejs/kit/commit/a2792e2cdc2dee91c20c7bfd921a104b539abae5), [`ba36148`](https://github.com/sveltejs/kit/commit/ba36148577ed0bc2675034a3d57001d71d328cd1), [`48e8710`](https://github.com/sveltejs/kit/commit/48e8710889b348a039322e05f3d2672327d56184), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`047d6a0`](https://github.com/sveltejs/kit/commit/047d6a0f1c3fd1bc6f90a81177a2834b023a64b3), [`87603d1`](https://github.com/sveltejs/kit/commit/87603d13d009cada171c9b5d5daad5adad4f72de), [`096962c`](https://github.com/sveltejs/kit/commit/096962c47b0b0f63acd0d539d3973797e4867487), [`d545970`](https://github.com/sveltejs/kit/commit/d54597018308068aea5268e6c45b26398cd57607), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`d06affc`](https://github.com/sveltejs/kit/commit/d06affc9246c676fc32b644efc0bbf53b42bc67d), [`8af47eb`](https://github.com/sveltejs/kit/commit/8af47eb06f9a1a883058de059ccf81aab4e85627), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`5c4d130`](https://github.com/sveltejs/kit/commit/5c4d130ea2953464d8b4b462eb5aa4641aa78da1), [`3f11f35`](https://github.com/sveltejs/kit/commit/3f11f35125c5b3625891f0d00d9c7c826e6eaea2), [`caf3a18`](https://github.com/sveltejs/kit/commit/caf3a18de6581ab7d454d6fa8ad37ebdbdef95e6), [`8823037`](https://github.com/sveltejs/kit/commit/8823037b4a341013cc40a9bb688ee23bda3dbb04), [`1d76212`](https://github.com/sveltejs/kit/commit/1d76212740f59ed58839ef569899c653870834fd), [`0dc0548`](https://github.com/sveltejs/kit/commit/0dc0548307a439b6a6f24b14405366ced213f828), [`00d81fa`](https://github.com/sveltejs/kit/commit/00d81fab618435bac95b1bacb5e9734b61ba1af6)]: + - @sveltejs/kit@3.0.0-next.0 + ## 3.0.10 ### Patch Changes diff --git a/packages/adapter-static/index.d.ts b/packages/adapter-static/index.d.ts index 101e02fdef12..ce080e34aa8a 100644 --- a/packages/adapter-static/index.d.ts +++ b/packages/adapter-static/index.d.ts @@ -1,10 +1,25 @@ import { Adapter } from '@sveltejs/kit'; export interface AdapterOptions { + /** The directory to write prerendered pages to. It defaults to `build`. */ pages?: string; + /** + * The directory to write static assets (the contents of `static`, plus client-side JS and CSS generated by SvelteKit) to. Ordinarily this should be the same as `pages`, and it will default to whatever the value of `pages` is, but in rare circumstances you might need to output pages and assets to separate locations. + */ assets?: string; + /** + * To create a [single page app (SPA)](https://svelte.dev/docs/kit/single-page-apps) you must specify the name of the fallback page to be generated by SvelteKit, which is used as the entry point for URLs that have not been prerendered. This is commonly `200.html`, but can vary depending on your deployment platform. You should avoid `index.html` where possible to avoid conflicting with a prerendered homepage. + * + * > [!NOTE] This option has large negative performance and SEO impacts. It is only recommended in certain circumstances such as wrapping the site in a mobile app. See the [single page apps](https://svelte.dev/docs/kit/single-page-apps) documentation for more details and alternatives. + */ fallback?: string; + /** + * If `true`, precompresses files with brotli and gzip. This will generate `.br` and `.gz` files. + */ precompress?: boolean; + /** + * By default, `adapter-static` checks that either all pages and endpoints (if any) of your app were prerendered, or you have the `fallback` option set. This check exists to prevent you from accidentally publishing an app where some parts of it are not accessible, because they are not contained in the final output. If you know this is ok (for example when a certain page only exists conditionally), you can set `strict` to `false` to turn off this check. + */ strict?: boolean; } diff --git a/packages/adapter-static/index.js b/packages/adapter-static/index.js index c2b87301c8a8..559b9fcef1bb 100644 --- a/packages/adapter-static/index.js +++ b/packages/adapter-static/index.js @@ -1,41 +1,53 @@ +import { rmSync } from 'node:fs'; import path from 'node:path'; import { platforms } from './platforms.js'; -/** @type {import('./index.js').default} */ +/** @type {typeof import('./index.js').default} */ export default function (options) { return { name: '@sveltejs/adapter-static', - /** @param {import('./internal.js').Builder2_0_0} builder */ async adapt(builder) { - if (!options?.fallback && builder.config.kit.router?.type !== 'hash') { + if (!options?.fallback && builder.config.router?.type !== 'hash') { const dynamic_routes = builder.routes.filter((route) => route.prerender !== true); if (dynamic_routes.length > 0 && options?.strict !== false) { - const prefix = path.relative('.', builder.config.kit.files.routes); + const prefix = path.relative('.', builder.config.files.routes); const has_param_routes = builder.routes.some((route) => route.id.includes('[')); - const config_option = - has_param_routes || JSON.stringify(builder.config.kit.prerender.entries) !== '["*"]' - ? ` - adjust the \`prerender.entries\` config option ${ - has_param_routes - ? '(routes with parameters are not part of entry points by default)' - : '' - } — see https://svelte.dev/docs/kit/configuration#prerender for more info.` - : ''; builder.log.error( - `@sveltejs/adapter-static: all routes must be fully prerenderable, but found the following routes that are dynamic: -${dynamic_routes.map((route) => ` - ${path.posix.join(prefix, route.id)}`).join('\n')} - -You have the following options: - - set the \`fallback\` option — see https://svelte.dev/docs/kit/single-page-apps#usage for more info. - - add \`export const prerender = true\` to your root \`+layout.js/.ts\` or \`+layout.server.js/.ts\` file. This will try to prerender all pages. - - add \`export const prerender = true\` to any \`+server.js/ts\` files that are not fetched by page \`load\` functions. -${config_option} - - pass \`strict: false\` to \`adapter-static\` to ignore this error. Only do this if you are sure you don't need the routes in question in your final app, as they will be unavailable. See https://github.com/sveltejs/kit/tree/main/packages/adapter-static#strict for more info. - -If this doesn't help, you may need to use a different adapter. @sveltejs/adapter-static can only be used for sites that don't need a server for dynamic rendering, and can run on just a static file server. -See https://svelte.dev/docs/kit/page-options#prerender for more details` + `\n@sveltejs/adapter-static: all routes must be fully prerenderable, but found the following routes that are dynamic: +${dynamic_routes.map((route) => ` - ${path.posix.join(prefix, route.id)}`).join('\n')}\n` ); - throw new Error('Encountered dynamic routes'); + + const options = [ + 'set the `fallback` option — see https://svelte.dev/docs/kit/single-page-apps#usage for more info.', + 'add `export const prerender = true` to your root `+layout.js/.ts` or `+layout.server.js/.ts` file. This will try to prerender all pages.', + 'add `export const prerender = true` to any `+server.js/ts` files that are not fetched by page `load` functions.' + ]; + + if (has_param_routes || JSON.stringify(builder.config.prerender.entries) !== '["*"]') { + let option = 'adjust the `prerender.entries` config option'; + if (has_param_routes) + option += ' (routes with parameters are not part of entry points by default)'; + options.push(option); + } + + options.push( + "pass `strict: false` to `adapter-static` to ignore this error. Only do this if you are sure you don't need the routes in question in your final app, as they will be unavailable. See https://github.com/sveltejs/kit/tree/main/packages/adapter-static#strict for more info." + ); + + builder.log( + `You have the following options:${options.map((o) => `\n - ${o}`).join('')}\n` + ); + + builder.log( + `If this doesn't help, you may need to use a different adapter. @sveltejs/adapter-static can only be used for sites that don't need a server for dynamic rendering, and can run on just a static file server.\n` + ); + + builder.log(`See https://svelte.dev/docs/kit/page-options#prerender for more details\n`); + + const error = new Error('Encountered dynamic routes'); + error.stack = ''; + throw error; } } @@ -52,15 +64,14 @@ See https://svelte.dev/docs/kit/page-options#prerender for more details` } const { - // @ts-ignore pages = 'build', assets = pages, fallback, precompress } = options ?? platform?.defaults ?? /** @type {import('./index.js').AdapterOptions} */ ({}); - builder.rimraf(assets); - builder.rimraf(pages); + rmSync(assets, { force: true, recursive: true }); + rmSync(pages, { force: true, recursive: true }); builder.generateEnvModule(); builder.writeClient(assets); diff --git a/packages/adapter-static/internal.d.ts b/packages/adapter-static/internal.d.ts deleted file mode 100644 index 9857dbae43cd..000000000000 --- a/packages/adapter-static/internal.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Utility type that makes all properties optional except for the specified keys - */ -export type PartialExcept = Partial> & Required>; - -/** - * We use a custom `Builder` type here to support the minimum version of SvelteKit. - */ -export type Builder2_0_0 = PartialExcept< - import('@sveltejs/kit').Builder, - | 'log' - | 'rimraf' - | 'mkdirp' - | 'config' - | 'prerendered' - | 'routes' - | 'createEntries' - | 'generateFallback' - | 'generateEnvModule' - | 'generateManifest' - | 'getBuildDirectory' - | 'getClientDirectory' - | 'getServerDirectory' - | 'getAppPath' - | 'writeClient' - | 'writePrerendered' - | 'writeServer' - | 'copy' - | 'compress' ->; diff --git a/packages/adapter-static/package.json b/packages/adapter-static/package.json index 488ef1d88c09..fe867f52ec77 100644 --- a/packages/adapter-static/package.json +++ b/packages/adapter-static/package.json @@ -1,6 +1,6 @@ { "name": "@sveltejs/adapter-static", - "version": "3.0.10", + "version": "4.0.0-next.4", "description": "Adapter for SvelteKit apps that prerenders your entire site as a collection of static files", "keywords": [ "adapter", @@ -33,21 +33,21 @@ "platforms.js" ], "scripts": { - "lint": "prettier --check .", + "lint": "oxfmt --check .", "check": "tsc", - "format": "pnpm lint --write", + "format": "oxfmt --write .", "test": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test" }, "devDependencies": { "@playwright/test": "catalog:", "@sveltejs/kit": "workspace:^", "@types/node": "catalog:", - "sirv": "^3.0.0", + "sirv": "^3.0.2", "svelte": "catalog:", - "typescript": "catalog:", + "typescript": "catalog:typescript-native", "vite": "catalog:" }, "peerDependencies": { - "@sveltejs/kit": "^2.0.0" + "@sveltejs/kit": "^3.0.0-next.0" } } diff --git a/packages/adapter-static/platforms.js b/packages/adapter-static/platforms.js index 6ed421625668..47c59903e0ab 100644 --- a/packages/adapter-static/platforms.js +++ b/packages/adapter-static/platforms.js @@ -6,12 +6,12 @@ import process from 'node:process'; * name: string; * test: () => boolean; * defaults: import('./index.js').AdapterOptions; - * done: (builder: import('./internal.js').Builder2_0_0) => void; + * done: (builder: import('@sveltejs/kit').Builder) => void; * }} * Platform */ // This function is duplicated in adapter-vercel -/** @param {import('./internal.js').Builder2_0_0} builder */ +/** @param {import('@sveltejs/kit').Builder} builder */ function static_vercel_config(builder) { /** @type {any[]} */ const prerendered_redirects = []; @@ -19,7 +19,17 @@ function static_vercel_config(builder) { /** @type {Record} */ const overrides = {}; - for (const [src, redirect] of builder.prerendered.redirects) { + for (let [src, redirect] of builder.prerendered.redirects) { + if (src.replace(/\/$/, '') === redirect.location.replace(/\/$/, '')) { + // ignore the extreme edge case of a `/foo` -> `/foo/` redirect, + // which would only arise if the response was generated by a + // `handle` hook or outside the app altogether (since you + // can't declaratively create both routes) + } else { + // redirect both `/foo` and `/foo/` to `redirect.location` + src = src.replace(/\/?$/, '/?'); + } + prerendered_redirects.push({ src, headers: { @@ -30,16 +40,24 @@ function static_vercel_config(builder) { } for (const [path, page] of builder.prerendered.pages) { - if (path.endsWith('/') && path !== '/') { + let overrides_path = path.slice(1); + + if (path !== '/') { + /** @type {string | undefined} */ + let counterpart_route = path + '/'; + + if (path.endsWith('/')) { + counterpart_route = path.slice(0, -1); + overrides_path = path.slice(1, -1); + } + prerendered_redirects.push( - { src: path, dest: path.slice(0, -1) }, - { src: path.slice(0, -1), status: 308, headers: { Location: path } } + { src: path, dest: counterpart_route }, + { src: counterpart_route, status: 308, headers: { Location: path } } ); - - overrides[page.file] = { path: path.slice(1, -1) }; - } else { - overrides[page.file] = { path: path.slice(1) }; } + + overrides[page.file] = { path: overrides_path }; } return { @@ -54,6 +72,19 @@ function static_vercel_config(builder) { }, { handle: 'filesystem' + }, + // Prevent incorrect caching: if a request to /_app/immutable/* doesn't match + // a static file, return 404 instead of falling through to dynamic routes. + // Otherwise, we could accidentally immutably cache dynamic content served + // by the fallback function. `no-store` stops the earlier immutable header + // from sticking to this 404, so a missing asset isn't cached for a year. + { + src: `/${builder.getAppPath()}/immutable/.+`, + status: 404, + headers: { + 'cache-control': 'no-store' + }, + continue: false } ], overrides diff --git a/packages/adapter-static/test/apps/prerendered/.npmrc b/packages/adapter-static/test/apps/prerendered/.npmrc deleted file mode 100644 index b6f27f135954..000000000000 --- a/packages/adapter-static/test/apps/prerendered/.npmrc +++ /dev/null @@ -1 +0,0 @@ -engine-strict=true diff --git a/packages/adapter-static/test/apps/prerendered/src/env.ts b/packages/adapter-static/test/apps/prerendered/src/env.ts new file mode 100644 index 000000000000..fa43d644dac4 --- /dev/null +++ b/packages/adapter-static/test/apps/prerendered/src/env.ts @@ -0,0 +1,8 @@ +import { defineEnvVars } from '@sveltejs/kit/env'; + +export const variables = defineEnvVars({ + PUBLIC_ANSWER: { + public: true, + static: true + } +}); diff --git a/packages/adapter-static/test/apps/prerendered/src/routes/public-env/+page.svelte b/packages/adapter-static/test/apps/prerendered/src/routes/public-env/+page.svelte index a36f01afaa54..4be4878c2a01 100644 --- a/packages/adapter-static/test/apps/prerendered/src/routes/public-env/+page.svelte +++ b/packages/adapter-static/test/apps/prerendered/src/routes/public-env/+page.svelte @@ -1,11 +1,10 @@

The answer is {PUBLIC_ANSWER}

{#if browser} -

The dynamic answer is {env.PUBLIC_ANSWER}

+

The dynamic answer is {PUBLIC_ANSWER}

{/if} diff --git a/packages/adapter-static/test/apps/prerendered/svelte.config.js b/packages/adapter-static/test/apps/prerendered/svelte.config.js deleted file mode 100644 index 20cd2b3ff5b8..000000000000 --- a/packages/adapter-static/test/apps/prerendered/svelte.config.js +++ /dev/null @@ -1,10 +0,0 @@ -import adapter from '../../../index.js'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter() - } -}; - -export default config; diff --git a/packages/adapter-static/test/apps/prerendered/vite.config.js b/packages/adapter-static/test/apps/prerendered/vite.config.js index 29ad08debe6a..dc6532d9578d 100644 --- a/packages/adapter-static/test/apps/prerendered/vite.config.js +++ b/packages/adapter-static/test/apps/prerendered/vite.config.js @@ -1,11 +1,16 @@ import { sveltekit } from '@sveltejs/kit/vite'; +import adapter from '../../../index.js'; /** @type {import('vite').UserConfig} */ const config = { build: { minify: false }, - plugins: [sveltekit()] + plugins: [ + sveltekit({ + adapter: adapter() + }) + ] }; export default config; diff --git a/packages/adapter-static/test/apps/spa/.npmrc b/packages/adapter-static/test/apps/spa/.npmrc deleted file mode 100644 index b6f27f135954..000000000000 --- a/packages/adapter-static/test/apps/spa/.npmrc +++ /dev/null @@ -1 +0,0 @@ -engine-strict=true diff --git a/packages/adapter-static/test/apps/spa/jsconfig.json b/packages/adapter-static/test/apps/spa/jsconfig.json index e0733f11157e..681df462f63f 100644 --- a/packages/adapter-static/test/apps/spa/jsconfig.json +++ b/packages/adapter-static/test/apps/spa/jsconfig.json @@ -1,4 +1,4 @@ { - "extends": "./.svelte-kit/tsconfig.json", + "extends": "$app/tsconfig", "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] } diff --git a/packages/adapter-static/test/apps/spa/src/env.ts b/packages/adapter-static/test/apps/spa/src/env.ts new file mode 100644 index 000000000000..fd970cbbea21 --- /dev/null +++ b/packages/adapter-static/test/apps/spa/src/env.ts @@ -0,0 +1,8 @@ +import { defineEnvVars } from '@sveltejs/kit/env'; + +export const variables = defineEnvVars({ + PUBLIC_VALUE: { + public: true, + static: true + } +}); diff --git a/packages/adapter-static/test/apps/spa/src/routes/fallback/[...rest]/+page.svelte b/packages/adapter-static/test/apps/spa/src/routes/fallback/[...rest]/+page.svelte index 55b4798e99c5..70ce9f5a8682 100644 --- a/packages/adapter-static/test/apps/spa/src/routes/fallback/[...rest]/+page.svelte +++ b/packages/adapter-static/test/apps/spa/src/routes/fallback/[...rest]/+page.svelte @@ -1,5 +1,5 @@

the fallback page was rendered

diff --git a/packages/adapter-static/test/apps/spa/svelte.config.js b/packages/adapter-static/test/apps/spa/svelte.config.js deleted file mode 100644 index da388bfc49d9..000000000000 --- a/packages/adapter-static/test/apps/spa/svelte.config.js +++ /dev/null @@ -1,12 +0,0 @@ -import adapter from '../../../index.js'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter({ - fallback: '200.html' - }) - } -}; - -export default config; diff --git a/packages/adapter-static/test/apps/spa/vite.config.js b/packages/adapter-static/test/apps/spa/vite.config.js index 29ad08debe6a..7e81282e251e 100644 --- a/packages/adapter-static/test/apps/spa/vite.config.js +++ b/packages/adapter-static/test/apps/spa/vite.config.js @@ -1,11 +1,18 @@ import { sveltekit } from '@sveltejs/kit/vite'; +import adapter from '../../../index.js'; /** @type {import('vite').UserConfig} */ const config = { build: { minify: false }, - plugins: [sveltekit()] + plugins: [ + sveltekit({ + adapter: adapter({ + fallback: '200.html' + }) + }) + ] }; export default config; diff --git a/packages/adapter-static/test/utils.js b/packages/adapter-static/test/utils.js index c553b42b7534..d4830c2f1a13 100644 --- a/packages/adapter-static/test/utils.js +++ b/packages/adapter-static/test/utils.js @@ -21,7 +21,7 @@ export const config = { ...devices['Desktop Chrome'], screenshot: 'only-on-failure', trace: 'retain-on-failure', - channel: 'chromium' + channel: 'chrome' }, workers: process.env.CI ? 2 : number_from_env('KIT_E2E_WORKERS', undefined), reporter: 'list', diff --git a/packages/adapter-static/tsconfig.json b/packages/adapter-static/tsconfig.json index 7395ffa36b2e..22ea3b1d6c08 100644 --- a/packages/adapter-static/tsconfig.json +++ b/packages/adapter-static/tsconfig.json @@ -11,5 +11,5 @@ }, "types": ["node"] }, - "include": ["index.js", "internal.d.ts", "test/utils.js"] + "include": ["index.js", "test/utils.js"] } diff --git a/packages/adapter-vercel/CHANGELOG.md b/packages/adapter-vercel/CHANGELOG.md index b8d36ea06184..d5235ccbbb9c 100644 --- a/packages/adapter-vercel/CHANGELOG.md +++ b/packages/adapter-vercel/CHANGELOG.md @@ -1,5 +1,96 @@ # @sveltejs/adapter-vercel +## 7.0.0-next.6 + +### Patch Changes + +- fix: match sibling `.html__route.js` route resolution requests ([#16674](https://github.com/sveltejs/kit/pull/16674)) +- Updated dependencies [[`10b2c64`](https://github.com/sveltejs/kit/commit/10b2c6419378b0340728f66d60c3fd824ae64c56)]: + - @sveltejs/kit@3.0.0-next.16 + +## 7.0.0-next.5 + +### Major Changes + +- chore: use `rolldown` for edge function bundling + +- breaking: edge function build target is now `es2022` + +- breaking: require `vite@^8.0.12`, the first Vite 8 release bundling stable `rolldown` 1.0.0 + +- breaking: remove support for edge and Node 20 runtimes + +### Patch Changes + +- chore: bump `@vercel/nft` dependency + +- chore: use `node:fs` instead of deprecated `builder.rimraf` and `builder.mkdirp` ([#16286](https://github.com/sveltejs/kit/pull/16286)) + +- fix: allow prerelease versions of SvelteKit 3 to satisfy the peer dependency range + +- fix: await `init` on every request to prevent race condition + +- chore: bump Rolldown to `1.2.0` +- Updated dependencies [[`bbd1e2f`](https://github.com/sveltejs/kit/commit/bbd1e2fcc9af58edb9846393b081be61df4789d5), [`e21d27b`](https://github.com/sveltejs/kit/commit/e21d27ba490c89da98dbad24fca364d0163137c4), [`6044d05`](https://github.com/sveltejs/kit/commit/6044d055031bc17deedc069dc4016565364bd4f4), [`7fb611f`](https://github.com/sveltejs/kit/commit/7fb611fb604e4d74cd889f5fe21ccd255f876382), [`9f06b3a`](https://github.com/sveltejs/kit/commit/9f06b3ac78be30d27b07fc00ea40bda493c573aa), [`8763327`](https://github.com/sveltejs/kit/commit/87633271ce40ac0839be9468598e965bde3cad51), [`7dc2379`](https://github.com/sveltejs/kit/commit/7dc23798cde97b5616049ba8d443a07e8c558bdc), [`a70c3eb`](https://github.com/sveltejs/kit/commit/a70c3eb31fe1e28f1532c84ec569ec7099da3344), [`89f49c7`](https://github.com/sveltejs/kit/commit/89f49c70b7fb4883cf704d2fb57d2d8595790ad2), [`f36b3a4`](https://github.com/sveltejs/kit/commit/f36b3a4cd307bd8c89e03fa191becc175e7b3077), [`1144a63`](https://github.com/sveltejs/kit/commit/1144a63d318c3c3255d06b374bb7a656a469fabf), [`9f06b3a`](https://github.com/sveltejs/kit/commit/9f06b3ac78be30d27b07fc00ea40bda493c573aa)]: + - @sveltejs/kit@3.0.0-next.15 + +## 7.0.0-next.4 + +### Major Changes + +- breaking: remove support for edge and Node 20 runtimes ([#16520](https://github.com/sveltejs/kit/pull/16520)) + +### Patch Changes + +- chore: bump `@vercel/nft` dependency ([#16522](https://github.com/sveltejs/kit/pull/16522)) +- Updated dependencies [[`6942ddb`](https://github.com/sveltejs/kit/commit/6942ddb68b43b5451337a8b420ce05a7a5de9919), [`68137d4`](https://github.com/sveltejs/kit/commit/68137d48cbf921264c6325ffa940165563c0b25a), [`15dc603`](https://github.com/sveltejs/kit/commit/15dc603f5027ee21b261c0c93f7decbf8f9498d8), [`a4f1521`](https://github.com/sveltejs/kit/commit/a4f15213c46f34aac88d05eb9af0905a5f776353), [`629b45d`](https://github.com/sveltejs/kit/commit/629b45d9e68846d862c10006fdabcc1526849811), [`7a7833a`](https://github.com/sveltejs/kit/commit/7a7833af90c0fa70c926cc84d61ca03f9db97bd2), [`e6591a0`](https://github.com/sveltejs/kit/commit/e6591a02743a2b324aa30cd463620450264fabae), [`7a7833a`](https://github.com/sveltejs/kit/commit/7a7833af90c0fa70c926cc84d61ca03f9db97bd2), [`b196a39`](https://github.com/sveltejs/kit/commit/b196a3965dd892a612cc6eae4868e450cc325736), [`66bf343`](https://github.com/sveltejs/kit/commit/66bf343453f8babf719193629a36f084d7bddf75), [`4bde76b`](https://github.com/sveltejs/kit/commit/4bde76b8a7ffa6885878d74eab3e542f1bda9c80), [`d9f05cb`](https://github.com/sveltejs/kit/commit/d9f05cb8efd8682bf91635f517f402849570cb20), [`40efca1`](https://github.com/sveltejs/kit/commit/40efca1b4afd6f0b6329bed652025b99edc8978c), [`8ef5548`](https://github.com/sveltejs/kit/commit/8ef5548f3e63ebb8371099a91acc182f7d094557), [`9efb8f2`](https://github.com/sveltejs/kit/commit/9efb8f2440328a536ebbb70a9e36ce4e30815e8a), [`6a670dc`](https://github.com/sveltejs/kit/commit/6a670dc778561e1fc504c2ccce864a46c2027dcf), [`3416dcc`](https://github.com/sveltejs/kit/commit/3416dccdceddf70ad229b5a1963d0735a7146c01), [`6850fa5`](https://github.com/sveltejs/kit/commit/6850fa56841afc8b1178fe16b6e61ffdfc854ab2), [`e6591a0`](https://github.com/sveltejs/kit/commit/e6591a02743a2b324aa30cd463620450264fabae), [`688bead`](https://github.com/sveltejs/kit/commit/688bead796f60db2384174e4f229c1d558a9ef00), [`b596610`](https://github.com/sveltejs/kit/commit/b596610a0c2e31e7d9adbdfb8d1354a165b5fe23), [`0f26c13`](https://github.com/sveltejs/kit/commit/0f26c133d3c5c19ed375f097b1eb27f6ce1928cd), [`110a3db`](https://github.com/sveltejs/kit/commit/110a3dbdba5f668d3e51b5e9f0d64be5a9ae5b39), [`665d467`](https://github.com/sveltejs/kit/commit/665d46720d26f545cf5ff52c86e44c9b2aa2521b), [`9c8c60c`](https://github.com/sveltejs/kit/commit/9c8c60c26c44e0705091de30a9a485b39d593def)]: + - @sveltejs/kit@3.0.0-next.13 + +## 7.0.0-next.3 + +### Patch Changes + +- fix: await `init` on every request to prevent race condition ([#16467](https://github.com/sveltejs/kit/pull/16467)) + +- chore: bump Rolldown to `1.2.0` ([#16455](https://github.com/sveltejs/kit/pull/16455)) +- Updated dependencies [[`adc4c5b`](https://github.com/sveltejs/kit/commit/adc4c5be542ecf7da93ee8d0e90e9d13b952998b), [`ecb0701`](https://github.com/sveltejs/kit/commit/ecb0701b8826e41229f84922f37c12099c66d3bc), [`4b4cc60`](https://github.com/sveltejs/kit/commit/4b4cc60990fa940236639a6cecb6c8efe4287bfa), [`7390dbe`](https://github.com/sveltejs/kit/commit/7390dbe6ef8dff0a1cd923f96daef462a3ce68f2), [`2e71340`](https://github.com/sveltejs/kit/commit/2e713407deb22965777afb01cb8215013ef8bfd3), [`c87bc3a`](https://github.com/sveltejs/kit/commit/c87bc3ac7830f8ee4e76254c9315adcc79ff6a99), [`af15c6c`](https://github.com/sveltejs/kit/commit/af15c6cab2fbfe6bae51dc1fa77a9271df996a47), [`c6fa431`](https://github.com/sveltejs/kit/commit/c6fa43157ae0dbaa9fc61b7e8d2db673e1f9f893), [`bfe4dea`](https://github.com/sveltejs/kit/commit/bfe4deae006f61347b5a5dce7747ec96692e97f9), [`df7dc72`](https://github.com/sveltejs/kit/commit/df7dc7219451bcc86a93e93188d78d664973d9ae), [`5ae11a1`](https://github.com/sveltejs/kit/commit/5ae11a119667c1b24e3b6d17f4b538032ddf1ab1), [`781205c`](https://github.com/sveltejs/kit/commit/781205c41cc5696eb862e5c13356c301a0368983), [`c87bc3a`](https://github.com/sveltejs/kit/commit/c87bc3ac7830f8ee4e76254c9315adcc79ff6a99), [`a1bfeb9`](https://github.com/sveltejs/kit/commit/a1bfeb9de906ae140b9cdc7d7eeaf0ee652a7bcc), [`c87bc3a`](https://github.com/sveltejs/kit/commit/c87bc3ac7830f8ee4e76254c9315adcc79ff6a99), [`9f0127d`](https://github.com/sveltejs/kit/commit/9f0127d4eee3f17dc1db1d2b99b1ca5fc80af784), [`4b4cc60`](https://github.com/sveltejs/kit/commit/4b4cc60990fa940236639a6cecb6c8efe4287bfa), [`8f5b9c7`](https://github.com/sveltejs/kit/commit/8f5b9c72ae930b3b93ef9e1b9ecb9e52664e1d74), [`c9b5544`](https://github.com/sveltejs/kit/commit/c9b554448633e1839e8c0f16c374b00a5ce4d1b6), [`fe1d4a4`](https://github.com/sveltejs/kit/commit/fe1d4a4d68d1d7a3925004fe804209ff54091e34), [`4b4cc60`](https://github.com/sveltejs/kit/commit/4b4cc60990fa940236639a6cecb6c8efe4287bfa), [`5f78e95`](https://github.com/sveltejs/kit/commit/5f78e953ae3966c23a01194dd200f5c57f723e40), [`25510fc`](https://github.com/sveltejs/kit/commit/25510fc79484363277d062e01f2b872f17a473f5), [`c99a6cf`](https://github.com/sveltejs/kit/commit/c99a6cfb7fec133aa85a6de59b43fc442aedc41a), [`17a45ca`](https://github.com/sveltejs/kit/commit/17a45cadb9356403b82c170f0d0027a1ba085667), [`2ca20c3`](https://github.com/sveltejs/kit/commit/2ca20c3ee7348836757dc6f3469217e46957766b), [`eb5c973`](https://github.com/sveltejs/kit/commit/eb5c9737b6d74c53f572b3e047faa5947052ced2)]: + - @sveltejs/kit@3.0.0-next.12 + +## 7.0.0-next.2 + +### Patch Changes + +- fix: allow prerelease versions of SvelteKit 3 to satisfy the peer dependency range ([#16286](https://github.com/sveltejs/kit/pull/16286)) +- Updated dependencies [[`737d119`](https://github.com/sveltejs/kit/commit/737d119730ec163832f838b3813c9cbb75dd799f), [`fa78efb`](https://github.com/sveltejs/kit/commit/fa78efb626031497eecc3a88fa7b6f6da36eca0f), [`07c207e`](https://github.com/sveltejs/kit/commit/07c207ed919570d4ed4954f751056b4f838abdea), [`a47071b`](https://github.com/sveltejs/kit/commit/a47071bb6581fe5f6df4cc47b80e9f0f594624e5), [`14d7d5a`](https://github.com/sveltejs/kit/commit/14d7d5abd9cd8462e4c79ed6a5c10bc43258e97a), [`5c38e51`](https://github.com/sveltejs/kit/commit/5c38e515db7fbb92e5ae01db84b4f0040a02f187), [`0702baa`](https://github.com/sveltejs/kit/commit/0702baa58b2a4600e83b55ef4a85e9cbe3391a4f), [`e1938c6`](https://github.com/sveltejs/kit/commit/e1938c62348b8ccc83e186b1acad76a0a10cc9ea), [`8293144`](https://github.com/sveltejs/kit/commit/8293144b6fbfff9f97cdb1d2224a5eaa04c84fc4), [`f76d7d9`](https://github.com/sveltejs/kit/commit/f76d7d91e18a0afd1f6798c095eb8c170c108cff), [`ab5c253`](https://github.com/sveltejs/kit/commit/ab5c253efb2546ead74db4c8c9b4c7639d97e0ed), [`b557b1b`](https://github.com/sveltejs/kit/commit/b557b1ba76903e1fb0fc3bb87bddc5420332137a), [`4a513e2`](https://github.com/sveltejs/kit/commit/4a513e27ab2ad44dae0c6a4a6c8deda840008530)]: + - @sveltejs/kit@3.0.0-next.8 + +## 7.0.0-next.1 +### Major Changes + + +- breaking: require `vite@^8.0.12`, the first Vite 8 release bundling stable `rolldown` 1.0.0 ([#16134](https://github.com/sveltejs/kit/pull/16134)) + + +### Patch Changes + +- Updated dependencies [[`a9629f1`](https://github.com/sveltejs/kit/commit/a9629f136d3256b14a8b3c10114c99832b977f6c), [`53d37f9`](https://github.com/sveltejs/kit/commit/53d37f93cf1f46995264398d4e54bb27bd1e75dd), [`dba6239`](https://github.com/sveltejs/kit/commit/dba62390e96f9f40c55ddc203744138664742026), [`ab12cb6`](https://github.com/sveltejs/kit/commit/ab12cb6a07269e2a18c0a3867e608e9838c592f3), [`f0eab52`](https://github.com/sveltejs/kit/commit/f0eab522bdea2d2a00f9b5473cd917d678b20daa), [`cd884c1`](https://github.com/sveltejs/kit/commit/cd884c1ffa6f96e08c70486c52fcca20501d5cef), [`9ed38a8`](https://github.com/sveltejs/kit/commit/9ed38a886be8c247c42c887c15d53b405693e1a9), [`53aa049`](https://github.com/sveltejs/kit/commit/53aa049a06ce7b053bdbe6484f05eecdb09d874e), [`984e57a`](https://github.com/sveltejs/kit/commit/984e57ac95d6ca7911611e1c297eecd1b1d870db), [`51785be`](https://github.com/sveltejs/kit/commit/51785be17ac706659d7fd7b3fca3fcf58dcc5de0), [`4f6bcbb`](https://github.com/sveltejs/kit/commit/4f6bcbbbb6e104238c41172ce7dbdaba486bbf43), [`78e7137`](https://github.com/sveltejs/kit/commit/78e71372ca284160df0aefe407999d46795a1299)]: + - @sveltejs/kit@3.0.0-next.5 + +## 7.0.0-next.0 +### Major Changes + + +- chore: use `rolldown` for edge function bundling ([#15432](https://github.com/sveltejs/kit/pull/15432)) + + +- breaking: edge function build target is now `es2022` ([#15432](https://github.com/sveltejs/kit/pull/15432)) + + +### Patch Changes + +- Updated dependencies [[`fa335bd`](https://github.com/sveltejs/kit/commit/fa335bd8af3a982d68c4d34b813b68887dc7cce8), [`3031d89`](https://github.com/sveltejs/kit/commit/3031d89087d49297e89b6566108a1a1105c38475), [`cb9d416`](https://github.com/sveltejs/kit/commit/cb9d416d05266b6237f2856fb94df5b8a9b04279), [`caf3a18`](https://github.com/sveltejs/kit/commit/caf3a18de6581ab7d454d6fa8ad37ebdbdef95e6), [`4777827`](https://github.com/sveltejs/kit/commit/4777827246727045cf0209399751377564e7951d), [`a2792e2`](https://github.com/sveltejs/kit/commit/a2792e2cdc2dee91c20c7bfd921a104b539abae5), [`ba36148`](https://github.com/sveltejs/kit/commit/ba36148577ed0bc2675034a3d57001d71d328cd1), [`48e8710`](https://github.com/sveltejs/kit/commit/48e8710889b348a039322e05f3d2672327d56184), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`047d6a0`](https://github.com/sveltejs/kit/commit/047d6a0f1c3fd1bc6f90a81177a2834b023a64b3), [`87603d1`](https://github.com/sveltejs/kit/commit/87603d13d009cada171c9b5d5daad5adad4f72de), [`096962c`](https://github.com/sveltejs/kit/commit/096962c47b0b0f63acd0d539d3973797e4867487), [`d545970`](https://github.com/sveltejs/kit/commit/d54597018308068aea5268e6c45b26398cd57607), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`d06affc`](https://github.com/sveltejs/kit/commit/d06affc9246c676fc32b644efc0bbf53b42bc67d), [`8af47eb`](https://github.com/sveltejs/kit/commit/8af47eb06f9a1a883058de059ccf81aab4e85627), [`e2f3075`](https://github.com/sveltejs/kit/commit/e2f3075ef299b91175fae116e6a76269002029cf), [`5c4d130`](https://github.com/sveltejs/kit/commit/5c4d130ea2953464d8b4b462eb5aa4641aa78da1), [`3f11f35`](https://github.com/sveltejs/kit/commit/3f11f35125c5b3625891f0d00d9c7c826e6eaea2), [`caf3a18`](https://github.com/sveltejs/kit/commit/caf3a18de6581ab7d454d6fa8ad37ebdbdef95e6), [`8823037`](https://github.com/sveltejs/kit/commit/8823037b4a341013cc40a9bb688ee23bda3dbb04), [`1d76212`](https://github.com/sveltejs/kit/commit/1d76212740f59ed58839ef569899c653870834fd), [`0dc0548`](https://github.com/sveltejs/kit/commit/0dc0548307a439b6a6f24b14405366ced213f828), [`00d81fa`](https://github.com/sveltejs/kit/commit/00d81fab618435bac95b1bacb5e9734b61ba1af6)]: + - @sveltejs/kit@3.0.0-next.0 + ## 6.3.4 ### Patch Changes diff --git a/packages/adapter-vercel/ambient.d.ts b/packages/adapter-vercel/ambient.d.ts deleted file mode 100644 index 67464d3365d9..000000000000 --- a/packages/adapter-vercel/ambient.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { RequestContext } from './index.js'; - -declare global { - namespace App { - export interface Platform { - /** - * `context` is only available in Edge Functions - * - * @deprecated Vercel's context is deprecated. Use [`@vercel/functions`](https://vercel.com/docs/functions/functions-api-reference/vercel-functions-package) instead. - */ - context?: RequestContext; - } - } -} diff --git a/packages/adapter-vercel/files/edge.js b/packages/adapter-vercel/files/edge.js deleted file mode 100644 index f87bb6e64a98..000000000000 --- a/packages/adapter-vercel/files/edge.js +++ /dev/null @@ -1,68 +0,0 @@ -/* eslint-disable n/prefer-global/process -- - Vercel Edge Runtime does not support node:process */ -import { Server } from 'SERVER'; -import { manifest } from 'MANIFEST'; - -const server = new Server(manifest); - -/** @type {HeadersInit | undefined} */ -let read_headers; -if (process.env.VERCEL_AUTOMATION_BYPASS_SECRET) { - read_headers = { - 'x-vercel-protection-bypass': process.env.VERCEL_AUTOMATION_BYPASS_SECRET - }; -} - -/** - * We don't know the origin until we receive a request, but - * that's guaranteed to happen before we call `read` - * @type {string} - */ -let origin; - -const initialized = server.init({ - env: /** @type {Record} */ (process.env), - read: async (file) => { - const url = `${origin}/${file}`; - const response = await fetch(url, { - // we need to add a bypass header if the user has deployment protection enabled - // see https://vercel.com/docs/deployment-protection/methods-to-bypass-deployment-protection/protection-bypass-automation - headers: read_headers - }); - - if (!response.ok) { - if (response.status === 401) { - throw new Error( - `Please enable Protection Bypass for Automation: https://svelte.dev/docs/kit/adapter-vercel#Troubleshooting-Deployment-protection` - ); - } - - // belt and braces — not sure how we could end up here - throw new Error( - `read(...) failed: could not fetch ${url} (${response.status} ${response.statusText})` - ); - } - - return response.body; - } -}); - -/** - * @param {Request} request - * @param {import('../index.js').RequestContext} context - */ -export default async (request, context) => { - if (!origin) { - origin = new URL(request.url).origin; - await initialized; - } - - return server.respond(request, { - getClientAddress() { - return /** @type {string} */ (request.headers.get('x-forwarded-for')); - }, - platform: { - context - } - }); -}; diff --git a/packages/adapter-vercel/files/serverless.js b/packages/adapter-vercel/files/serverless.js index fb818ba067a5..d6aa640edf16 100644 --- a/packages/adapter-vercel/files/serverless.js +++ b/packages/adapter-vercel/files/serverless.js @@ -6,7 +6,7 @@ import process from 'node:process'; const server = new Server(manifest); await server.init({ - env: /** @type {Record} */ (process.env), + env: process.env, read: createReadableStream }); diff --git a/packages/adapter-vercel/index.d.ts b/packages/adapter-vercel/index.d.ts index e3333d04a88e..8cbdafd1b070 100644 --- a/packages/adapter-vercel/index.d.ts +++ b/packages/adapter-vercel/index.d.ts @@ -1,28 +1,25 @@ import { Adapter } from '@sveltejs/kit'; -import './ambient.js'; -import { RuntimeConfigKey } from './utils.js'; +import { RuntimeKey } from './utils.js'; export default function plugin(config?: Config): Adapter; export interface ServerlessConfig { /** - * Whether to use [Edge Functions](https://vercel.com/docs/concepts/functions/edge-functions) (`'edge'`) or [Serverless Functions](https://vercel.com/docs/concepts/functions/serverless-functions) (`'nodejs22.x'`, `'nodejs24.x'` etc). + * Which [Serverless Function](https://vercel.com/docs/concepts/functions/serverless-functions) runtime to use (`'nodejs22.x'`, `'nodejs24.x'` etc). * @default Same as the build environment */ - runtime?: Exclude; + runtime?: RuntimeKey; /** - * To which regions to deploy the app. A list of regions. + * A list of regions to deploy the app to * More info: https://vercel.com/docs/concepts/edge-network/regions */ regions?: string[]; /** * Maximum execution duration (in seconds) that will be allowed for the Serverless Function. - * Serverless only. */ maxDuration?: number; /** * Amount of memory (RAM in MB) that will be allocated to the Serverless Function. - * Serverless only. */ memory?: number; /** @@ -32,7 +29,6 @@ export interface ServerlessConfig { /** * [Incremental Static Regeneration](https://vercel.com/docs/concepts/incremental-static-regeneration/overview) configuration. - * Serverless only. */ isr?: | { @@ -75,89 +71,9 @@ type ImagesConfig = { contentDispositionType?: string; }; -/** @deprecated */ -export interface EdgeConfig { - /** - * Whether to use [Edge Functions](https://vercel.com/docs/concepts/functions/edge-functions) (`'edge'`) or [Serverless Functions](https://vercel.com/docs/concepts/functions/serverless-functions) (`'nodejs22.x'`, `'nodejs24.x'` etc). - */ - runtime?: 'edge'; - /** - * To which regions to deploy the app. A list of regions or `'all'`. - * More info: https://vercel.com/docs/concepts/edge-network/regions - */ - regions?: string[] | 'all'; - /** - * List of packages that should not be bundled into the Edge Function. - * Edge only. - */ - external?: string[]; - /** - * If `true`, this route will always be deployed as its own separate function - */ - split?: boolean; -} - -export type Config = (EdgeConfig | ServerlessConfig) & { +export type Config = ServerlessConfig & { /** * https://vercel.com/docs/build-output-api/v3/configuration#images */ images?: ImagesConfig; }; - -// we copy the RequestContext interface from `@vercel/edge` because that package can't co-exist with `@types/node`. -// see https://github.com/sveltejs/kit/pull/9280#issuecomment-1452110035 - -/** - * An extension to the standard `Request` object that is passed to every Edge Function. - * - * @deprecated - use [`@vercel/functions`](https://vercel.com/docs/functions/functions-api-reference/vercel-functions-package) instead. - * - * @example - * ```ts - * import type { RequestContext } from '@vercel/edge'; - * - * export default async function handler(request: Request, ctx: RequestContext): Promise { - * // ctx is the RequestContext - * } - * ``` - */ -export interface RequestContext { - /** - * A method that can be used to keep the function running after a response has been sent. - * This is useful when you have an async task that you want to keep running even after the - * response has been sent and the request has ended. - * - * @example - * - * Sending an internal error to an error tracking service - * - * ```ts - * import type { RequestContext } from '@vercel/edge'; - * - * export async function handleRequest(request: Request, ctx: RequestContext): Promise { - * try { - * return await myFunctionThatReturnsResponse(); - * } catch (e) { - * ctx.waitUntil((async () => { - * // report this error to your error tracking service - * await fetch('https://my-error-tracking-service.com', { - * method: 'POST', - * body: JSON.stringify({ - * stack: e.stack, - * message: e.message, - * name: e.name, - * url: request.url, - * }), - * }); - * })()); - * return new Response('Internal Server Error', { status: 500 }); - * } - * } - * ``` - */ - waitUntil( - /** - * A promise that will be kept alive until it resolves or rejects. - */ promise: Promise - ): void; -} diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 9369f8cbb026..126f1d2a5090 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -1,54 +1,29 @@ -/** @import { BuildOptions } from 'esbuild' */ import fs from 'node:fs'; import path from 'node:path'; import process from 'node:process'; import { fileURLToPath } from 'node:url'; +import { VERSION } from '@sveltejs/kit'; import { nodeFileTrace } from '@vercel/nft'; -import esbuild from 'esbuild'; import { get_pathname, parse_isr_expiration, pattern_to_src, resolve_runtime } from './utils.js'; -import { VERSION } from '@sveltejs/kit'; - -/** - * @template T - * @template {keyof T} K - * @typedef {Partial> & Required>} PartialExcept - */ -/** - * We use a custom `Builder` type here to support the minimum version of SvelteKit. - * @typedef {PartialExcept} Builder2_4_0 - */ - -const name = '@sveltejs/adapter-vercel'; const INTERNAL = '![-]'; // this name is guaranteed not to conflict with user routes -const [kit_major, kit_minor] = VERSION.split('.'); - -// https://vercel.com/docs/functions/edge-functions/edge-runtime#compatible-node.js-modules -const compatible_node_modules = ['async_hooks', 'events', 'buffer', 'assert', 'util']; - -/** @type {import('./index.js').default} **/ +/** @type {typeof import('./index.js').default} **/ const plugin = function (defaults = {}) { - if ('edge' in defaults) { - throw new Error("{ edge: true } has been removed in favour of { runtime: 'edge' }"); + // @ts-ignore TODO remove this in a future version + if ('edge' in defaults || defaults.runtime === 'edge') { + throw new Error('The `edge` runtime is no longer supported'); } return { - name, - /** @param {Builder2_4_0} builder */ + name: '@sveltejs/adapter-vercel', + /** @param {import('@sveltejs/kit').Builder} builder */ async adapt(builder) { - if (!builder.routes) { - throw new Error( - '@sveltejs/adapter-vercel >=2.x (possibly installed through @sveltejs/adapter-auto) requires @sveltejs/kit version 1.5 or higher. ' + - 'Either downgrade the adapter or upgrade @sveltejs/kit' - ); - } - const dir = '.vercel/output'; const tmp = builder.getBuildDirectory('vercel-tmp'); - builder.rimraf(dir); - builder.rimraf(tmp); + fs.rmSync(dir, { force: true, recursive: true }); + fs.rmSync(tmp, { force: true, recursive: true }); if (fs.existsSync('vercel.json')) { const vercel_file = fs.readFileSync('vercel.json', 'utf-8'); @@ -59,7 +34,7 @@ const plugin = function (defaults = {}) { const files = fileURLToPath(new URL('./files', import.meta.url).href); const dirs = { - static: `${dir}/static${builder.config.kit.paths.base}`, + static: `${dir}/static${builder.config.paths.base}`, functions: `${dir}/functions` }; @@ -87,8 +62,8 @@ const plugin = function (defaults = {}) { MANIFEST: './manifest.js' } }); - if (builder.hasServerInstrumentationFile?.()) { - builder.instrument?.({ + if (builder.hasServerInstrumentationFile()) { + builder.instrument({ entrypoint: `${tmp}/index.js`, instrumentation: `${builder.getServerDirectory()}/instrumentation.server.js` }); @@ -107,153 +82,6 @@ const plugin = function (defaults = {}) { } } - let warned = false; - - /** - * @param {string} name - * @param {import('./index.js').EdgeConfig} config - * @param {import('@sveltejs/kit').RouteDefinition[]} routes - */ - async function generate_edge_function(name, config, routes) { - if (!warned) { - warned = true; - builder.log.warn( - `The \`runtime: 'edge'\` option is deprecated, and will be removed in a future version of adapter-vercel` - ); - } - - const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`); - const relativePath = path.posix.relative(tmp, builder.getServerDirectory()); - - builder.copy(`${files}/edge.js`, `${tmp}/edge.js`, { - replace: { - SERVER: `${relativePath}/index.js`, - MANIFEST: './manifest.js' - } - }); - - write( - `${tmp}/manifest.js`, - `export const manifest = ${builder.generateManifest({ relativePath, routes })};\n` - ); - - try { - const outdir = `${dirs.functions}/${name}.func`; - /** @type {BuildOptions} */ - const esbuild_config = { - // minimum Node.js version supported is v14.6.0 that is mapped to ES2019 - // https://edge-runtime.vercel.app/features/polyfills - // TODO verify the latest ES version the edge runtime supports - target: 'es2020', - bundle: true, - platform: 'browser', - conditions: [ - // Vercel's Edge runtime key https://runtime-keys.proposal.wintercg.org/#edge-light - 'edge-light', - // re-include these since they are included by default when no conditions are specified - // https://esbuild.github.io/api/#conditions - 'module' - ], - format: 'esm', - external: [ - ...compatible_node_modules, - ...compatible_node_modules.map((id) => `node:${id}`), - ...(config.external || []) - ], - sourcemap: 'linked', - banner: { js: 'globalThis.global = globalThis;' }, - loader: { - '.wasm': 'copy', - '.woff': 'copy', - '.woff2': 'copy', - '.ttf': 'copy', - '.eot': 'copy', - '.otf': 'copy' - } - }; - const result = await esbuild.build({ - entryPoints: [`${tmp}/edge.js`], - outfile: `${outdir}/index.js`, - ...esbuild_config - }); - - let instrumentation_result; - if (builder.hasServerInstrumentationFile?.()) { - instrumentation_result = await esbuild.build({ - entryPoints: [`${builder.getServerDirectory()}/instrumentation.server.js`], - outfile: `${outdir}/instrumentation.server.js`, - ...esbuild_config - }); - - builder.instrument?.({ - entrypoint: `${outdir}/index.js`, - instrumentation: `${outdir}/instrumentation.server.js`, - module: { - generateText: generate_traced_edge_module - } - }); - } - - const warnings = instrumentation_result - ? [...result.warnings, ...instrumentation_result.warnings] - : result.warnings; - - if (warnings.length > 0) { - const formatted = await esbuild.formatMessages(warnings, { - kind: 'warning', - color: true - }); - - console.error(formatted.join('\n')); - } - } catch (err) { - const error = /** @type {import('esbuild').BuildFailure} */ (err); - for (const e of error.errors) { - for (const node of e.notes) { - const match = - /The package "(.+)" wasn't found on the file system but is built into node/.exec( - node.text - ); - - if (match) { - node.text = `Cannot use "${match[1]}" when deploying to Vercel Edge Functions.`; - } - } - } - - const formatted = await esbuild.formatMessages(error.errors, { - kind: 'error', - color: true - }); - - console.error(formatted.join('\n')); - - throw new Error( - `Bundling with esbuild failed with ${error.errors.length} ${ - error.errors.length === 1 ? 'error' : 'errors' - }`, - { cause: err } - ); - } - - write( - `${dirs.functions}/${name}.func/.vc-config.json`, - JSON.stringify( - { - runtime: config.runtime, - regions: config.regions, - entrypoint: 'index.js', - framework: { - slug: 'sveltekit', - version: VERSION - } - }, - null, - '\t' - ) - ); - } - /** @type {Map[] }>} */ const groups = new Map(); @@ -271,7 +99,12 @@ const plugin = function (defaults = {}) { // group routes by config for (const route of builder.routes) { + if (route.config.runtime === 'edge') { + throw new Error('The `edge` runtime is no longer supported'); + } + const runtime = resolve_runtime(defaults.runtime, route.config.runtime); + const config = { ...defaults, ...route.config, runtime }; if (is_prerendered(route)) { @@ -282,13 +115,7 @@ const plugin = function (defaults = {}) { } if (config.isr) { - const directory = path.relative('.', builder.config.kit.files.routes + route.id); - - if (runtime === 'edge') { - throw new Error( - `${directory}: Routes using \`isr\` must use a Node.js or Bun runtime (for example 'nodejs24.x' or 'experimental_bun1.x')` - ); - } + const directory = path.relative('.', builder.config.files.routes + route.id); if (config.isr.allowQuery?.includes('__pathname')) { throw new Error( @@ -348,13 +175,10 @@ const plugin = function (defaults = {}) { const singular = groups.size === 1; for (const group of groups.values()) { - const generate_function = - group.config.runtime === 'edge' ? generate_edge_function : generate_serverless_function; - // generate one function for the group const name = singular ? `${INTERNAL}/catchall` : `${INTERNAL}/${group.i}`; - await generate_function( + await generate_serverless_function( name, /** @type {any} */ (group.config), /** @type {import('@sveltejs/kit').RouteDefinition[]} */ (group.routes) @@ -370,17 +194,15 @@ const plugin = function (defaults = {}) { // by SvelteKit rather than Vercel const runtime = resolve_runtime(defaults.runtime); - const generate_function = - runtime === 'edge' ? generate_edge_function : generate_serverless_function; - await generate_function( + await generate_serverless_function( `${INTERNAL}/catchall`, /** @type {any} */ ({ ...defaults, runtime }), [] ); } - if (builder.config.kit.experimental.remoteFunctions) { + if (builder.config.experimental.remoteFunctions) { // Ensure remote functions are always handled by the catchall route, which will be symlinked to /_app/remote. // This stops them from being affected by ISR config from other routes that match /[...rest] (ref: #15085) // and also makes them show as handled by `/_app/remote` in Vercel's observability. @@ -393,7 +215,7 @@ const plugin = function (defaults = {}) { const target = path.join(dirs.functions, INTERNAL, 'catchall.func'); // Ensure the parent directory exists before symlinking - builder.mkdirp(path.join(dirs.functions, app_path)); + fs.mkdirSync(path.join(dirs.functions, app_path), { recursive: true }); const relative = path.relative(path.dirname(remote_symlink_path), target); @@ -416,7 +238,7 @@ const plugin = function (defaults = {}) { if (isr) { const isr_name = route.id.slice(1) || '__root__'; // should we check that __root__ isn't a route? const base = `${dirs.functions}/${isr_name}`; - builder.mkdirp(base); + fs.mkdirSync(base, { recursive: true }); const target = `${dirs.functions}/${name}.func`; const relative = path.relative(path.dirname(base), target); @@ -467,7 +289,7 @@ const plugin = function (defaults = {}) { const target = path.join(dirs.functions, `${name}.func`); // The actual function directory e.g., .vercel/output/functions/![-].func // Ensure the directory for the data endpoint symlink exists (e.g., functions/index/) - builder.mkdirp(base_dir); + fs.mkdirSync(base_dir, { recursive: true }); // Calculate relative paths FROM the directory containing the symlink TO the target const relative_for_main = path.relative(path.dirname(main_symlink_path), target); @@ -486,23 +308,21 @@ const plugin = function (defaults = {}) { } } - // optional chaining to support older versions that don't have this setting yet - if (builder.config.kit.router?.resolution === 'server') { - // Create a separate edge function just for server-side route resolution. + if (builder.config.router.resolution === 'server') { + // Create a separate serverless function just for server-side route resolution. // By omitting all routes we're ensuring it's small (the routes will still be available // to the route resolution, because it does not rely on the server routing manifest) - await generate_edge_function( - `${builder.config.kit.appDir}/route`, - { - external: 'external' in defaults ? defaults.external : undefined, - runtime: 'edge' - }, + const runtime = resolve_runtime(defaults.runtime); + + await generate_serverless_function( + `${builder.config.appDir}/route`, + /** @type {any} */ ({ ...defaults, runtime }), [] ); static_config.routes.push({ - src: `${builder.config.kit.paths.base}/(|.+/)__route\\.js`, - dest: `${builder.config.kit.paths.base}/${builder.config.kit.appDir}/route` + src: `${builder.config.paths.base}/(?:.+/|.+\\.html)?__route\\.js`, + dest: `${builder.config.paths.base}/${builder.config.appDir}/route` }); } @@ -516,28 +336,16 @@ const plugin = function (defaults = {}) { }, supports: { - read: ({ config, route }) => { - const runtime = config.runtime ?? defaults.runtime; - - // TODO bump peer dep in next adapter major to simplify this - if (runtime === 'edge' && kit_major === '2' && kit_minor < '25') { - throw new Error( - `${name}: Cannot use \`read\` from \`$app/server\` in route \`${route.id}\` configured with \`runtime: 'edge'\` and SvelteKit < 2.25.0` - ); - } - - return true; - }, + read: () => true, instrumentation: () => true } }; }; -/** @param {import('./index.js').EdgeConfig & import('./index.js').ServerlessConfig} config */ +/** @param {import('./index.js').ServerlessConfig} config */ function hash_config(config) { return [ config.runtime ?? '', - config.external ?? '', config.regions ?? '', config.memory ?? '', config.maxDuration ?? '', @@ -561,7 +369,7 @@ function write(file, data) { // This function is duplicated in adapter-static /** - * @param {Builder2_4_0} builder + * @param {import('@sveltejs/kit').Builder} builder * @param {import('./index.js').Config} config * @param {string} dir */ @@ -651,7 +459,7 @@ function static_vercel_config(builder, config, dir) { } ], headers: { - 'Set-Cookie': `__vdpl=${process.env.VERCEL_DEPLOYMENT_ID}; Path=${builder.config.kit.paths.base}/; SameSite=Strict; Secure; HttpOnly` + 'Set-Cookie': `__vdpl=${process.env.VERCEL_DEPLOYMENT_ID}; Path=${builder.config.paths.base}/; SameSite=Strict; Secure; HttpOnly` }, continue: true }); @@ -660,7 +468,7 @@ function static_vercel_config(builder, config, dir) { // allows you to set multiple cookies for a single route. essentially, since we // know that the entry file will be requested immediately, we can set the second // cookie in _that_ response rather than the document response - const base = `${dir}/${builder.config.kit.appDir}/immutable/entry`; + const base = `${dir}/${builder.config.appDir}/immutable/entry`; const entry = fs.readdirSync(base).find((file) => file.startsWith('start.')); if (!entry) { @@ -703,7 +511,7 @@ function static_vercel_config(builder, config, dir) { } /** - * @param {Builder2_4_0} builder + * @param {import('@sveltejs/kit').Builder} builder * @param {string} entry * @param {string} dir * @param {import('./index.js').ServerlessConfig} config @@ -824,18 +632,10 @@ async function create_function_bundle(builder, entry, dir, config) { } /** - * - * @param {Builder2_4_0} builder - * @param {any} vercel_config + * @param {import('@sveltejs/kit').Builder} builder + * @param {any} vercel_config see https://vercel.com/docs/project-configuration/vercel-json */ function validate_vercel_json(builder, vercel_config) { - if (builder.routes.length > 0 && !builder.routes[0].api) { - // bail — we're on an older SvelteKit version that doesn't - // populate `route.api.methods`, so we can't check - // to see if cron paths are valid - return; - } - const crons = /** @type {Array} */ ( Array.isArray(vercel_config?.crons) ? vercel_config.crons : [] ); @@ -882,23 +682,4 @@ function is_prerendered(route) { ); } -/** - * @param {{ instrumentation: string; start: string }} opts - */ -function generate_traced_edge_module({ instrumentation, start }) { - return `\ -import './${instrumentation}'; -const promise = import('./${start}'); - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - */ -export default async (req, res) => { - const { default: handler } = await promise; - return handler(req, res); -} -`; -} - export default plugin; diff --git a/packages/adapter-vercel/package.json b/packages/adapter-vercel/package.json index f79963ba1fa6..113fe1c9d8d4 100644 --- a/packages/adapter-vercel/package.json +++ b/packages/adapter-vercel/package.json @@ -1,6 +1,6 @@ { "name": "@sveltejs/adapter-vercel", - "version": "6.3.4", + "version": "7.0.0-next.6", "description": "A SvelteKit adapter that creates a Vercel app", "keywords": [ "adapter", @@ -30,29 +30,26 @@ "files", "index.js", "utils.js", - "index.d.ts", - "ambient.d.ts" + "index.d.ts" ], "scripts": { - "lint": "prettier --check .", - "format": "pnpm lint --write", + "lint": "oxfmt --check .", + "format": "oxfmt --write .", "check": "tsc", - "test": "vitest run" + "test:unit": "vitest run", + "test:build": "cd test/apps/basic && pnpm build", + "test": "pnpm test:unit && pnpm test:build" }, "dependencies": { - "@vercel/nft": "^1.3.2", - "esbuild": "^0.28.1" + "@vercel/nft": "^1.10.2" }, "devDependencies": { "@sveltejs/kit": "workspace:^", "@types/node": "catalog:", - "typescript": "catalog:", + "typescript": "catalog:typescript-native", "vitest": "catalog:" }, "peerDependencies": { - "@sveltejs/kit": "^2.4.0" - }, - "engines": { - "node": ">=20.0" + "@sveltejs/kit": "^3.0.0-next.0" } } diff --git a/packages/adapter-vercel/test/apps/basic/package.json b/packages/adapter-vercel/test/apps/basic/package.json index 821ffb4d56c1..808087e7676c 100644 --- a/packages/adapter-vercel/test/apps/basic/package.json +++ b/packages/adapter-vercel/test/apps/basic/package.json @@ -4,8 +4,8 @@ "private": true, "scripts": { "dev": "vite dev", + "preview": "vite preview", "build": "vite build", - "prepare": "svelte-kit sync || echo ''", "test:platform": "playwright test" }, "devDependencies": { diff --git a/packages/adapter-vercel/test/apps/basic/svelte.config.js b/packages/adapter-vercel/test/apps/basic/svelte.config.js deleted file mode 100644 index 20cd2b3ff5b8..000000000000 --- a/packages/adapter-vercel/test/apps/basic/svelte.config.js +++ /dev/null @@ -1,10 +0,0 @@ -import adapter from '../../../index.js'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - adapter: adapter() - } -}; - -export default config; diff --git a/packages/adapter-vercel/test/apps/basic/test/test.ts b/packages/adapter-vercel/test/apps/basic/test/test.ts index 008bd735358f..1f54da741c61 100644 --- a/packages/adapter-vercel/test/apps/basic/test/test.ts +++ b/packages/adapter-vercel/test/apps/basic/test/test.ts @@ -116,3 +116,24 @@ test('client-side navigation to prerendered routes works', async ({ page }) => { await page.click('a[href="/prerendered"]'); await expect(page.locator('h1')).toContainText('this page is prerendered'); }); + +test('missing immutable asset returns 404 with no-store cache-control', async ({ request }) => { + // https://github.com/sveltejs/kit/pull/16077 — the 404 must not inherit the + // immutable `max-age=31536000` header, otherwise it's cached for a year + const response = await request.get('/_app/immutable/chunks/nonexistent.js'); + expect(response.status()).toBe(404); + expect(response.headers()['cache-control']).toBe('no-store'); +}); + +test('valid immutable asset is still cached immutably', async ({ page, request }) => { + await page.goto('/'); + // immutable assets are preloaded via , not + +{@render children()} diff --git a/packages/adapter-vercel/test/apps/split/src/routes/+page.svelte b/packages/adapter-vercel/test/apps/split/src/routes/+page.svelte new file mode 100644 index 000000000000..57d50b6f11b3 --- /dev/null +++ b/packages/adapter-vercel/test/apps/split/src/routes/+page.svelte @@ -0,0 +1,5 @@ +

Hello from SvelteKit on Vercel

+ +
diff --git a/packages/adapter-vercel/test/apps/split/src/routes/a/+page.svelte b/packages/adapter-vercel/test/apps/split/src/routes/a/+page.svelte new file mode 100644 index 000000000000..445d9a0d621b --- /dev/null +++ b/packages/adapter-vercel/test/apps/split/src/routes/a/+page.svelte @@ -0,0 +1 @@ +

This route will be served by a different function because we use the split config

diff --git a/packages/adapter-vercel/test/apps/split/test/test.ts b/packages/adapter-vercel/test/apps/split/test/test.ts new file mode 100644 index 000000000000..95250e9e1d71 --- /dev/null +++ b/packages/adapter-vercel/test/apps/split/test/test.ts @@ -0,0 +1,8 @@ +import { expect, test } from '@playwright/test'; + +test('split functions work', async ({ page }) => { + await page.goto('/'); + await expect(page.locator('h1')).toContainText('Hello from SvelteKit on Vercel'); + await page.goto('/a'); + await expect(page.locator('p')).toContainText(/split config/); +}); diff --git a/packages/adapter-vercel/test/apps/split/tsconfig.json b/packages/adapter-vercel/test/apps/split/tsconfig.json new file mode 100644 index 000000000000..619fe2dd9d72 --- /dev/null +++ b/packages/adapter-vercel/test/apps/split/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "$app/tsconfig", + "compilerOptions": { + "rewriteRelativeImportExtensions": true, + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "moduleResolution": "bundler" + }, + "include": ["src", "test", "playwright.config.js", "vite.config.ts"] +} diff --git a/packages/adapter-vercel/test/apps/split/vite.config.ts b/packages/adapter-vercel/test/apps/split/vite.config.ts new file mode 100644 index 000000000000..0b40b6378475 --- /dev/null +++ b/packages/adapter-vercel/test/apps/split/vite.config.ts @@ -0,0 +1,13 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; +import adapter from '../../../index.js'; + +export default defineConfig({ + plugins: [ + sveltekit({ + adapter: adapter({ + split: true + }) + }) + ] +}); diff --git a/packages/adapter-vercel/test/utils.js b/packages/adapter-vercel/test/utils.js index 18c9c56d89d7..b5bdbb0a952e 100644 --- a/packages/adapter-vercel/test/utils.js +++ b/packages/adapter-vercel/test/utils.js @@ -16,7 +16,7 @@ export const config = { baseURL: process.env.DEPLOYMENT_URL, screenshot: 'only-on-failure', trace: 'retain-on-failure', - channel: 'chromium' + channel: 'chrome' }, workers: process.env.CI ? 2 : undefined, reporter: 'list', diff --git a/packages/adapter-vercel/test/utils.spec.js b/packages/adapter-vercel/test/utils.spec.js index 2ee4ca2030c0..537930079a65 100644 --- a/packages/adapter-vercel/test/utils.spec.js +++ b/packages/adapter-vercel/test/utils.spec.js @@ -176,12 +176,12 @@ describe('parse_isr_expiration', () => { describe('resolve_runtime', () => { test('prefers override_key over default_key', () => { - const result = resolve_runtime('nodejs20.x', 'experimental_bun1.x'); + const result = resolve_runtime('nodejs20.x', 'bun1.x'); assert.equal(result, 'bun1.x'); }); test('uses default_key when override_key is undefined', () => { - const result = resolve_runtime('experimental_bun1.x'); + const result = resolve_runtime('bun1.x'); assert.equal(result, 'bun1.x'); }); diff --git a/packages/adapter-vercel/utils.js b/packages/adapter-vercel/utils.js index d35ceea65198..e6570fd0e0fd 100644 --- a/packages/adapter-vercel/utils.js +++ b/packages/adapter-vercel/utils.js @@ -113,37 +113,47 @@ export function parse_isr_expiration(value, route_id) { * @returns {RuntimeKey} */ export function resolve_runtime(default_key, override_key) { - const key = (override_key ?? default_key ?? get_default_runtime()).replace('experimental_', ''); + const key = override_key ?? default_key ?? get_default_runtime(); assert_is_valid_runtime(key); return key; } -const valid_node_versions = [20, 22, 24]; -const formatter = new Intl.ListFormat('en', { type: 'disjunction' }); +const valid_node_versions = [22, 24]; +const formatter = new Intl.ListFormat('en-gb', { type: 'disjunction' }); /** @returns {RuntimeKey} */ function get_default_runtime() { - // TODO may someday need to auto-detect Bun, but this will be complicated because you may want to run your build - // with Bun but not have your serverless runtime be in Bun. Vercel will likely have to attach something to `globalThis` or similar - // to tell us what the bun configuration is. - const major = Number(process.version.slice(1).split('.')[0]); + // if the user ran e.g. `bunx --bun vite build`, infer that they want to run the app in Bun + if (process.versions.bun) { + const major = process.versions.bun.split('.')[0]; + if (major !== '1') { + throw new Error( + `Unsupported Bun version: ${major}. Please use Bun 1.x to build your project, or explicitly specify a runtime in your adapter configuration.` + ); + } - if (!valid_node_versions.includes(major)) { - throw new Error( - `Unsupported Node.js version: ${process.version}. Please use Node ${formatter.format(valid_node_versions.map((v) => `${v}`))} to build your project, or explicitly specify a runtime in your adapter configuration.` - ); + return `bun${major}.x`; + } + + // otherwise, default to the version of Node specified in the project config + if (process.versions.node) { + const major = Number(process.versions.node.split('.')[0]); + + if (!valid_node_versions.includes(major)) { + throw new Error( + `Unsupported Node.js version: ${process.version}. Please use Node ${formatter.format(valid_node_versions.map((v) => `${v}`))} to build your project, or explicitly specify a runtime in your adapter configuration.` + ); + } + + return `nodejs${/** @type {22 | 24} */ (major)}.x`; } - return `nodejs${/** @type {20 | 22 | 24} */ (major)}.x`; + throw new Error( + 'Could not auto-detect a runtime. Please explicitly specify a runtime in your adapter configuration.' + ); } -const valid_runtimes = /** @type {const} */ ([ - 'nodejs20.x', - 'nodejs22.x', - 'nodejs24.x', - 'bun1.x', - 'edge' -]); +const valid_runtimes = /** @type {const} */ (['nodejs22.x', 'nodejs24.x', 'bun1.x']); /** * @param {string} key @@ -157,5 +167,4 @@ function assert_is_valid_runtime(key) { } } -/** @typedef {Exclude | 'experimental_bun1.x'} RuntimeConfigKey */ /** @typedef {typeof valid_runtimes[number]} RuntimeKey */ diff --git a/packages/adapter-vercel/vitest.config.js b/packages/adapter-vercel/vitest.config.js new file mode 100644 index 000000000000..a15b3a470a6d --- /dev/null +++ b/packages/adapter-vercel/vitest.config.js @@ -0,0 +1,5 @@ +// we need this file to prevent Vitest from resolving a Vitest config from another directory + +import { defineConfig } from 'vitest/config'; + +export default defineConfig({}); diff --git a/packages/amp/CHANGELOG.md b/packages/amp/CHANGELOG.md deleted file mode 100644 index 336e7c6fe82b..000000000000 --- a/packages/amp/CHANGELOG.md +++ /dev/null @@ -1,92 +0,0 @@ -# @sveltejs/amp - -## 1.1.5 -### Patch Changes - - -- chore: add `.git` to the end of `package.json` repository url ([#14134](https://github.com/sveltejs/kit/pull/14134)) - -- Updated dependencies [[`c968aef`](https://github.com/sveltejs/kit/commit/c968aef5727f978244d5160657b4a7ac651384ae)]: - - @sveltejs/kit@2.27.3 - -## 1.1.4 -### Patch Changes - - -- docs: update URLs for new svelte.dev site ([#12857](https://github.com/sveltejs/kit/pull/12857)) - -- Updated dependencies [[`dcbe4222a194c5f90cfc0fc020cf065f7a4e4c46`](https://github.com/sveltejs/kit/commit/dcbe4222a194c5f90cfc0fc020cf065f7a4e4c46), [`4cdbf76fbbf0c0ce7f574ef69c8daddcf954d39d`](https://github.com/sveltejs/kit/commit/4cdbf76fbbf0c0ce7f574ef69c8daddcf954d39d), [`3a9b78f04786898ca93f6d4b75ab18d26bc45192`](https://github.com/sveltejs/kit/commit/3a9b78f04786898ca93f6d4b75ab18d26bc45192), [`723eb8b31e6a22c82f730c30e485386c8676b746`](https://github.com/sveltejs/kit/commit/723eb8b31e6a22c82f730c30e485386c8676b746), [`8ec471c875345b751344e67580ff1b772ef2735b`](https://github.com/sveltejs/kit/commit/8ec471c875345b751344e67580ff1b772ef2735b)]: - - @sveltejs/kit@2.7.3 - -## 1.1.3 -### Patch Changes - - -- chore: configure provenance in a simpler manner ([#12570](https://github.com/sveltejs/kit/pull/12570)) - -- Updated dependencies [[`087a43d391fc38b8c008fb39a804dc6988974101`](https://github.com/sveltejs/kit/commit/087a43d391fc38b8c008fb39a804dc6988974101)]: - - @sveltejs/kit@2.5.22 - -## 1.1.2 -### Patch Changes - - -- chore: package provenance ([#12567](https://github.com/sveltejs/kit/pull/12567)) - -- Updated dependencies [[`4930a8443caa53bcecee7b690cd28e429b1c8a20`](https://github.com/sveltejs/kit/commit/4930a8443caa53bcecee7b690cd28e429b1c8a20)]: - - @sveltejs/kit@2.5.21 - -## 1.1.1 - -### Patch Changes - -- chore: add keywords for discovery in npm search ([#12330](https://github.com/sveltejs/kit/pull/12330)) - -- Updated dependencies [[`25acb1d9fce998dccd8050b93cf4142c2b082611`](https://github.com/sveltejs/kit/commit/25acb1d9fce998dccd8050b93cf4142c2b082611), [`642c4a4aff4351b786fe6274aa2f0bf7d905faf9`](https://github.com/sveltejs/kit/commit/642c4a4aff4351b786fe6274aa2f0bf7d905faf9), [`0a0e9aa897123ebec50af08e9385b2ca4fc5bb28`](https://github.com/sveltejs/kit/commit/0a0e9aa897123ebec50af08e9385b2ca4fc5bb28)]: - - @sveltejs/kit@2.5.11 - -## 1.1.0 - -### Minor Changes - -- feat: allow SvelteKit 2 as peer dependency ([#11233](https://github.com/sveltejs/kit/pull/11233)) - -## 1.0.2 - -### Patch Changes - -- fix: publish missing files ([#8532](https://github.com/sveltejs/kit/pull/8532)) - -## 1.0.1 - -### Patch Changes - -- chore: remove superfluous main field from package.json ([#8519](https://github.com/sveltejs/kit/pull/8519)) - -- Updated dependencies [[`7e2d3405`](https://github.com/sveltejs/kit/commit/7e2d34056e99f371e22406d941b764df365a2649)]: - - @sveltejs/kit@1.1.1 - -## 1.0.0 - -### Major Changes - -First major release, see below for the history of changes that lead up to this. -Starting from now all releases follow semver and changes will be listed as Major/Minor/Patch - -## 1.0.0-next.3 - -### Patch Changes - -- chore: add peerDependencies, add more specific next version ([#8141](https://github.com/sveltejs/kit/pull/8141)) - -## 1.0.0-next.2 - -### Patch Changes - -- Amend boilerplate to match current recommendations ([#7184](https://github.com/sveltejs/kit/pull/7184)) - -## 1.0.0-next.1 - -### Patch Changes - -- Add types to pkg.exports ([#5045](https://github.com/sveltejs/kit/pull/5045)) diff --git a/packages/amp/index.d.ts b/packages/amp/index.d.ts deleted file mode 100644 index 475cec2b75c3..000000000000 --- a/packages/amp/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export function transform(html: string): string; diff --git a/packages/amp/index.js b/packages/amp/index.js deleted file mode 100644 index 0c1baedb1838..000000000000 --- a/packages/amp/index.js +++ /dev/null @@ -1,28 +0,0 @@ -// https://amp.dev/documentation/guides-and-tutorials/learn/spec/amp-boilerplate/ -const boilerplate = ` - - - -`; - -/** @param {string} html */ -export function transform(html) { - return html - .replace(//, (_match, $1) => ``) - ).css; - }; - const SKIP_PARSING_REGEX = /url\(/i; /** Capture a single url(...) so we can process them one at a time */ @@ -31,8 +22,6 @@ const HASH_OR_QUERY_REGEX = /[#?]/; */ const VITE_ASSET_PREFIX = './'; -const AST_OFFSET = '`; - }); - - css = dropcss({ css, html: markup }).css; - return markup.replace('', `${css}`); - } - } - }); -} diff --git a/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control/+page.js b/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control/+page.js deleted file mode 100644 index feb67e3e4c07..000000000000 --- a/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control/+page.js +++ /dev/null @@ -1,8 +0,0 @@ -export const prerender = true; - -/** @type {import('./$types').PageLoad} */ -export function load({ setHeaders }) { - setHeaders({ - 'cache-control': 'public, max-age=300' - }); -} diff --git a/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control/+page.svelte b/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control/+page.svelte deleted file mode 100644 index 63edda10a55f..000000000000 --- a/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control/+page.svelte +++ /dev/null @@ -1 +0,0 @@ -

the cache-control headers should be removed from this page

diff --git a/packages/kit/test/apps/amp/src/routes/invalid/+page.svelte b/packages/kit/test/apps/amp/src/routes/invalid/+page.svelte deleted file mode 100644 index cfb5e5dd32a5..000000000000 --- a/packages/kit/test/apps/amp/src/routes/invalid/+page.svelte +++ /dev/null @@ -1,2 +0,0 @@ - -invalid diff --git a/packages/kit/test/apps/amp/src/routes/invalid/has-stylesheet/+page.svelte b/packages/kit/test/apps/amp/src/routes/invalid/has-stylesheet/+page.svelte deleted file mode 100644 index e6c9b2bdb3c2..000000000000 --- a/packages/kit/test/apps/amp/src/routes/invalid/has-stylesheet/+page.svelte +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/kit/test/apps/amp/src/routes/origin.json/+server.js b/packages/kit/test/apps/amp/src/routes/origin.json/+server.js deleted file mode 100644 index 142be28ffbca..000000000000 --- a/packages/kit/test/apps/amp/src/routes/origin.json/+server.js +++ /dev/null @@ -1,6 +0,0 @@ -import { json } from '@sveltejs/kit'; - -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET({ url }) { - return json({ origin: url.origin }); -} diff --git a/packages/kit/test/apps/amp/src/routes/origin/+page.js b/packages/kit/test/apps/amp/src/routes/origin/+page.js deleted file mode 100644 index 298a00a667d7..000000000000 --- a/packages/kit/test/apps/amp/src/routes/origin/+page.js +++ /dev/null @@ -1,10 +0,0 @@ -/** @type {import('@sveltejs/kit').Load} */ -export async function load({ url, fetch }) { - const res = await fetch('/origin.json'); - const data = await res.json(); - - return { - origin: url.origin, - data - }; -} diff --git a/packages/kit/test/apps/amp/src/routes/origin/+page.svelte b/packages/kit/test/apps/amp/src/routes/origin/+page.svelte deleted file mode 100644 index ad574575c080..000000000000 --- a/packages/kit/test/apps/amp/src/routes/origin/+page.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -

{data.origin}

-

{page.url.origin}

-

{data.data.origin}

diff --git a/packages/kit/test/apps/amp/src/routes/styles/+page.svelte b/packages/kit/test/apps/amp/src/routes/styles/+page.svelte deleted file mode 100644 index 08db544b1211..000000000000 --- a/packages/kit/test/apps/amp/src/routes/styles/+page.svelte +++ /dev/null @@ -1,16 +0,0 @@ - - -{#if Math.random() <= 1} -

this text is rendered

-{:else} - -{/if} - - diff --git a/packages/kit/test/apps/amp/src/routes/styles/Unused.svelte b/packages/kit/test/apps/amp/src/routes/styles/Unused.svelte deleted file mode 100644 index 76039659e1e2..000000000000 --- a/packages/kit/test/apps/amp/src/routes/styles/Unused.svelte +++ /dev/null @@ -1,7 +0,0 @@ -

this component will never be rendered

- - diff --git a/packages/kit/test/apps/amp/src/routes/styles/imported.css b/packages/kit/test/apps/amp/src/routes/styles/imported.css deleted file mode 100644 index 9a9f3c1f3279..000000000000 --- a/packages/kit/test/apps/amp/src/routes/styles/imported.css +++ /dev/null @@ -1,3 +0,0 @@ -.shouty { - text-transform: uppercase; -} diff --git a/packages/kit/test/apps/amp/src/routes/valid.json/+server.js b/packages/kit/test/apps/amp/src/routes/valid.json/+server.js deleted file mode 100644 index 714189a71ae4..000000000000 --- a/packages/kit/test/apps/amp/src/routes/valid.json/+server.js +++ /dev/null @@ -1,5 +0,0 @@ -import { json } from '@sveltejs/kit'; - -export function GET() { - return json({ answer: 42 }); -} diff --git a/packages/kit/test/apps/amp/src/routes/valid/+page.js b/packages/kit/test/apps/amp/src/routes/valid/+page.js deleted file mode 100644 index aaf112e513dc..000000000000 --- a/packages/kit/test/apps/amp/src/routes/valid/+page.js +++ /dev/null @@ -1,6 +0,0 @@ -/** @type {import('@sveltejs/kit').Load} */ -export async function load({ fetch }) { - const res = await fetch('/valid.json'); - const { answer } = await res.json(); - return { answer }; -} diff --git a/packages/kit/test/apps/amp/src/routes/valid/+page.svelte b/packages/kit/test/apps/amp/src/routes/valid/+page.svelte deleted file mode 100644 index 17bca5973e70..000000000000 --- a/packages/kit/test/apps/amp/src/routes/valid/+page.svelte +++ /dev/null @@ -1,16 +0,0 @@ - - -

Hello from the {browser ? 'client' : 'server'} in {dev ? 'dev' : 'prod'} mode!

-

The answer is {data.answer}

-

This text is red

- - diff --git a/packages/kit/test/apps/amp/svelte.config.js b/packages/kit/test/apps/amp/svelte.config.js deleted file mode 100644 index 18aec126f11f..000000000000 --- a/packages/kit/test/apps/amp/svelte.config.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - inlineStyleThreshold: Infinity - } -}; - -export default config; diff --git a/packages/kit/test/apps/amp/test/test.js b/packages/kit/test/apps/amp/test/test.js deleted file mode 100644 index 06e0b8977c8f..000000000000 --- a/packages/kit/test/apps/amp/test/test.js +++ /dev/null @@ -1,91 +0,0 @@ -import fs from 'node:fs'; -import process from 'node:process'; -import { expect } from '@playwright/test'; -import { test } from '../../../utils.js'; - -test.describe.configure({ mode: 'parallel' }); - -test('renders an AMP page', async ({ page, baseURL }) => { - await page.goto(`${baseURL}/valid`); - - await expect(page.locator('h1')).toHaveText( - `Hello from the server in ${process.env.DEV ? 'dev' : 'prod'} mode!` - ); - - await expect(page.locator('h2')).toHaveText('The answer is 42'); - - // should not include serialized data - expect(await page.$('script[sveltekit\\:data-type="data"]')).toBeNull(); -}); - -test('styles are applied', async ({ page, baseURL, get_computed_style }) => { - await page.goto(`${baseURL}/valid`); - - expect(await get_computed_style('p', 'color')).toEqual('rgb(255, 0, 0)'); - expect(await get_computed_style('footer', 'color')).toEqual('rgb(128, 0, 128)'); -}); - -test('sets origin', async ({ baseURL, page }) => { - const { origin } = new URL(/** @type {string} */ (baseURL)); - - await page.goto(`${baseURL}/origin`); - - await expect(page.locator('[data-source="load"]')).toHaveText(origin); - await expect(page.locator('[data-source="store"]')).toHaveText(origin); - await expect(page.locator('[data-source="endpoint"]')).toHaveText(origin); -}); - -test('only includes CSS for rendered components', async ({ page, baseURL }) => { - await page.goto(`${baseURL}/styles`); - - const style = await page.innerHTML('style[amp-custom]'); - - expect(style).toContain('#ff3e00'); // rendered styles - expect(style).toContain('uppercase'); // imported styles - expect(style).not.toContain('#40b3ff'); // unrendered styles -}); - -test('http-equiv tags are removed', async ({ page }) => { - await page.goto('/http-equiv/cache-control'); - - expect(await page.textContent('h1')).toBe( - 'the cache-control headers should be removed from this page' - ); - expect(await page.innerHTML('head')).not.toContain('http-equiv="cache-control"'); -}); - -// validation tests are skipped because amphtml-validator doesn't -// play nicely with CI, and is also abominably slow, because -// everything AMP-related is awful -test.skip('prints validation errors', async ({ page, baseURL }) => { - await page.goto(`${baseURL}/invalid`); - - const body = await page.innerHTML('body'); - - expect(body).toContain("Invalid URL protocol 'javascript:' for attribute 'href' in tag 'a'"); -}); - -test.skip('throws error on encountering stylesheet links', async ({ page }) => { - await page.goto('/invalid/has-stylesheet'); - - expect(await page.textContent('body')).toContain( - 'An AMP document cannot contain — ensure that inlineStyleThreshold is set to Infinity, and remove links from your page template and elements' - ); -}); - -test('skips client build if every node has CSR disabled', async ({ page, request }) => { - test.skip(!!process.env.DEV); - - const files = fs.existsSync('.svelte-kit/output/client/_app/immutable/nodes'); - expect(files).toBe(false); - - await page.goto('/assets'); - const src = await page.locator('img').getAttribute('src'); - if (!src) throw new Error('Image src not found on /assets page'); - - const img_response = await request.get(src); - expect(img_response.status()).toBe(200); - - const public_asset_response = await request.get('/asset.json'); - expect(public_asset_response.status()).toBe(200); -}); diff --git a/packages/kit/test/apps/amp/tsconfig.json b/packages/kit/test/apps/amp/tsconfig.json deleted file mode 100644 index b1096bf168cd..000000000000 --- a/packages/kit/test/apps/amp/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "noEmit": true - }, - "extends": "./.svelte-kit/tsconfig.json" -} diff --git a/packages/kit/test/apps/async/_test_dependencies/plain-lib/index.js b/packages/kit/test/apps/async/_test_dependencies/plain-lib/index.js new file mode 100644 index 000000000000..9776a321c5f1 --- /dev/null +++ b/packages/kit/test/apps/async/_test_dependencies/plain-lib/index.js @@ -0,0 +1 @@ +export { create_key_set } from './jwks/remote.js'; diff --git a/packages/kit/test/apps/async/_test_dependencies/plain-lib/jwks/remote.js b/packages/kit/test/apps/async/_test_dependencies/plain-lib/jwks/remote.js new file mode 100644 index 000000000000..401a09256b98 --- /dev/null +++ b/packages/kit/test/apps/async/_test_dependencies/plain-lib/jwks/remote.js @@ -0,0 +1,3 @@ +export function create_key_set(url) { + return `key set for ${url}`; +} diff --git a/packages/kit/test/apps/async/_test_dependencies/plain-lib/package.json b/packages/kit/test/apps/async/_test_dependencies/plain-lib/package.json new file mode 100644 index 000000000000..2f53775ab3a1 --- /dev/null +++ b/packages/kit/test/apps/async/_test_dependencies/plain-lib/package.json @@ -0,0 +1,8 @@ +{ + "name": "e2e-test-dep-plain", + "version": "1.0.0", + "private": true, + "type": "module", + "main": "index.js", + "svelte": "./index.js" +} diff --git a/packages/kit/test/apps/async/_test_dependencies/remote-lib/data.remote.js b/packages/kit/test/apps/async/_test_dependencies/remote-lib/data.remote.js new file mode 100644 index 000000000000..80f2cf9816bf --- /dev/null +++ b/packages/kit/test/apps/async/_test_dependencies/remote-lib/data.remote.js @@ -0,0 +1,3 @@ +import { query } from '$app/server'; + +export const lib_echo = query('unchecked', (value) => `lib says ${value}`); diff --git a/packages/kit/test/apps/async/_test_dependencies/remote-lib/index.js b/packages/kit/test/apps/async/_test_dependencies/remote-lib/index.js new file mode 100644 index 000000000000..f4bb682cc0e8 --- /dev/null +++ b/packages/kit/test/apps/async/_test_dependencies/remote-lib/index.js @@ -0,0 +1 @@ +export { lib_echo } from './data.remote.js'; diff --git a/packages/kit/test/apps/async/_test_dependencies/remote-lib/package.json b/packages/kit/test/apps/async/_test_dependencies/remote-lib/package.json new file mode 100644 index 000000000000..f5832a14d750 --- /dev/null +++ b/packages/kit/test/apps/async/_test_dependencies/remote-lib/package.json @@ -0,0 +1,11 @@ +{ + "name": "e2e-test-dep-remote", + "version": "1.0.0", + "private": true, + "type": "module", + "main": "index.js", + "svelte": "./index.js", + "peerDependencies": { + "@sveltejs/kit": "*" + } +} diff --git a/packages/kit/test/apps/async/package.json b/packages/kit/test/apps/async/package.json index 0a2094fca12e..e324e3fa096c 100644 --- a/packages/kit/test/apps/async/package.json +++ b/packages/kit/test/apps/async/package.json @@ -4,22 +4,28 @@ "version": "0.0.1", "type": "module", "scripts": { - "dev": "vite dev", + "dev": "svelte-kit sync && vite dev", "build": "vite build", "preview": "vite preview", - "prepare": "svelte-kit sync || echo ''", - "check": "svelte-kit sync && tsc && svelte-check", + "check": "svelte-kit sync && node node_modules/@typescript/native/bin/tsc && svelte-check --tsgo --incremental", "test": "pnpm test:dev && pnpm test:build", "test:dev": "DEV=true playwright test", - "test:build": "playwright test" + "test:build": "svelte-kit sync && vitest run && playwright test" }, "devDependencies": { "@sveltejs/kit": "workspace:^", "@sveltejs/vite-plugin-svelte": "catalog:", "svelte": "catalog:", "svelte-check": "catalog:", + "@typescript/native": "catalog:", "typescript": "catalog:", "valibot": "catalog:", - "vite": "catalog:" + "vite": "catalog:", + "e2e-test-dep-plain": "file:./_test_dependencies/plain-lib", + "e2e-test-dep-remote": "file:./_test_dependencies/remote-lib" + }, + "imports": { + "#lib": "./src/lib/index.js", + "#lib/*": "./src/lib/*" } } diff --git a/packages/kit/test/apps/async/src/env.ts b/packages/kit/test/apps/async/src/env.ts new file mode 100644 index 000000000000..82f110e72262 --- /dev/null +++ b/packages/kit/test/apps/async/src/env.ts @@ -0,0 +1,20 @@ +import { defineEnvVars } from '@sveltejs/kit/env'; + +export const variables = defineEnvVars({ + PRIVATE_STATIC: { + public: false, + static: true + }, + PRIVATE_DYNAMIC: { + public: false, + static: false + }, + PUBLIC_STATIC: { + public: true, + static: true + }, + PUBLIC_DYNAMIC: { + public: true, + static: false + } +}); diff --git a/packages/kit/test/apps/async/src/hooks.client.js b/packages/kit/test/apps/async/src/hooks.client.js index af343e2d1022..598e971f83fd 100644 --- a/packages/kit/test/apps/async/src/hooks.client.js +++ b/packages/kit/test/apps/async/src/hooks.client.js @@ -1,13 +1,17 @@ import { isRedirect } from '@sveltejs/kit'; -/** @type {import('@sveltejs/kit').HandleClientError} */ -export const handleError = ({ error: e, event, status, message }) => { +/** @type {import('@sveltejs/kit/hooks').HandleClientError} */ +export const handleError = (input) => { // helps us catch sveltekit redirects thrown in component code - if (isRedirect(e)) { + if (isRedirect(input.error)) { throw new Error("Redirects shouldn't trigger the handleError hook"); } - const error = /** @type {Error} */ (e); + if (input.kind !== 'unknown') return input.error; - return { message: `${error.message} (${status} ${message}, on ${event.url.pathname})` }; + const error = /** @type {Error} */ (input.error); + + return { + message: `${error.message} (500 Internal Error, on ${input.event.url.pathname})` + }; }; diff --git a/packages/kit/test/apps/async/src/hooks.js b/packages/kit/test/apps/async/src/hooks.js index 238cc7744f2c..96240b3cc14f 100644 --- a/packages/kit/test/apps/async/src/hooks.js +++ b/packages/kit/test/apps/async/src/hooks.js @@ -1,6 +1,6 @@ -import { Foo } from '$lib'; +import { Foo } from '#lib'; -/** @type {import("@sveltejs/kit").Transport} */ +/** @type {import("@sveltejs/kit/hooks").Transport} */ export const transport = { Foo: { encode: (value) => value instanceof Foo && [value.message], diff --git a/packages/kit/test/apps/async/src/hooks.server.js b/packages/kit/test/apps/async/src/hooks.server.js index 3e9c141e2f37..0c246c2eba65 100644 --- a/packages/kit/test/apps/async/src/hooks.server.js +++ b/packages/kit/test/apps/async/src/hooks.server.js @@ -1,14 +1,14 @@ import { isRedirect } from '@sveltejs/kit'; import { do_something } from './routes/remote/server-action/action.remote'; -/** @type {import('@sveltejs/kit').Handle} */ +/** @type {import('@sveltejs/kit/hooks').Handle} */ export async function handle({ event, resolve }) { // Assign each browser session a unique id so that the in-memory `count` // state in `routes/remote/query-command.remote.js` is isolated per test. // Without this, tests running in parallel (different Playwright workers) // against the same server clobber each other's state and flake. - if (!event.cookies.get('count_session')) { - event.cookies.set('count_session', crypto.randomUUID(), { + if (!event.cookies.get('session')) { + event.cookies.set('session', crypto.randomUUID(), { path: '/', httpOnly: true, sameSite: 'lax' @@ -38,31 +38,24 @@ export async function handle({ event, resolve }) { return resolve(event); } -/** @type {import('@sveltejs/kit').HandleValidationError} */ -export const handleValidationError = ({ issues }) => { - return { message: issues[0].message }; -}; - -/** @type {import('@sveltejs/kit').HandleServerError} */ -export const handleError = ({ error: e, event, status, message }) => { +/** @type {import('@sveltejs/kit/hooks').HandleServerError} */ +export const handleError = (input) => { // helps us catch sveltekit redirects thrown in component code - if (isRedirect(e)) { + if (isRedirect(input.error)) { throw new Error("Redirects shouldn't trigger the handleError hook"); } - const error = /** @type {Error} */ (e); + if (input.kind === 'validation') { + // must not throw, even when validation failed inside a query + void input.event.url.pathname; + return { message: input.issues[0].message }; + } - return { message: `${error.message} (${status} ${message}, on ${event.url.pathname})` }; -}; + if (input.kind !== 'unknown') return input.error; + + const error = /** @type {Error} */ (input.error); -// TODO: remove in SvelteKit 3.0 -// @ts-ignore this doesn't exist in old Node -Promise.withResolvers ??= () => { - /** @type {{ promise: Promise, resolve: (value: any) => void, reject: (reason?: any) => void }} */ - const d = {}; - d.promise = new Promise((resolve, reject) => { - d.resolve = resolve; - d.reject = reject; - }); - return d; + return { + message: `${error.message} (500 Internal Error, on ${input.event.url.pathname})` + }; }; diff --git a/packages/kit/test/apps/async/src/routes/+error.svelte b/packages/kit/test/apps/async/src/routes/+error.svelte index 31f3cb324e47..0d3cab0c999b 100644 --- a/packages/kit/test/apps/async/src/routes/+error.svelte +++ b/packages/kit/test/apps/async/src/routes/+error.svelte @@ -12,6 +12,8 @@

This is your custom error page saying: "{error.message}"

+home + diff --git a/packages/kit/test/apps/basics/src/routes/asset-preload/prerendered/+page.server.js b/packages/kit/test/apps/options-2/src/routes/asset-preload/prerendered/+page.server.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/asset-preload/prerendered/+page.server.js rename to packages/kit/test/apps/options-2/src/routes/asset-preload/prerendered/+page.server.js diff --git a/packages/kit/test/apps/basics/src/routes/asset-preload/prerendered/+page.svelte b/packages/kit/test/apps/options-2/src/routes/asset-preload/prerendered/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/asset-preload/prerendered/+page.svelte rename to packages/kit/test/apps/options-2/src/routes/asset-preload/prerendered/+page.svelte diff --git a/packages/kit/test/apps/options-2/src/routes/asset-preload/shlop.woff2 b/packages/kit/test/apps/options-2/src/routes/asset-preload/shlop.woff2 new file mode 100644 index 000000000000..6e1af4e75189 Binary files /dev/null and b/packages/kit/test/apps/options-2/src/routes/asset-preload/shlop.woff2 differ diff --git a/packages/kit/test/apps/options-2/src/routes/asset-preload/styles.css b/packages/kit/test/apps/options-2/src/routes/asset-preload/styles.css new file mode 100644 index 000000000000..fef546dd088d --- /dev/null +++ b/packages/kit/test/apps/options-2/src/routes/asset-preload/styles.css @@ -0,0 +1,5 @@ +@font-face { + /* we need a font file that exceeds assetsInlineLimit */ + font-family: 'Shlop'; + src: url(./shlop.woff2); +} diff --git a/packages/kit/test/apps/options-2/src/routes/deeply/nested/page/+page.svelte b/packages/kit/test/apps/options-2/src/routes/deeply/nested/page/+page.svelte index f645edd52e3d..6c2701fa0f46 100644 --- a/packages/kit/test/apps/options-2/src/routes/deeply/nested/page/+page.svelte +++ b/packages/kit/test/apps/options-2/src/routes/deeply/nested/page/+page.svelte @@ -4,8 +4,8 @@

Hello

-

base: {resolve('/')}

-

assets: {asset('/')}

+

base: {resolve('')}

+

assets: {asset('answer.txt').replace('answer.txt', '')}