Skip to content

feat: add $app/manifest module - #16372

Merged
Rich-Harris merged 25 commits into
version-3from
app-manifest
Jul 20, 2026
Merged

feat: add $app/manifest module#16372
Rich-Harris merged 25 commits into
version-3from
app-manifest

Conversation

@Rich-Harris

@Rich-Harris Rich-Harris commented Jul 16, 2026

Copy link
Copy Markdown
Member

Part of #16159. This adds an $app/manifest module containing information about the app — it replaces the equivalents in $service-worker (so that we can delete that module).

It exports four things:

  • immutable is an array of { path: string } objects representing the contents of _app/immutable (empty in dev)
  • files is an array of { path: string } objects representing the contents of static
  • prerendered is an array of { path: string } objects representing all the prerendered pages/endpoints in your app (empty in dev, and during prerendering)
  • routes is an array of { id: string } objects representing the routes of your app

Paths are relative to base. In a service worker, you can do e.g. cache.add(path) as part of an offline strategy.


Please don't delete this checklist! Before submitting the PR, please make sure you do the following:

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.

Tests

  • Run the tests with pnpm test and lint the project with pnpm lint and pnpm check

Changesets

  • If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running pnpm changeset and following the prompts. Changesets that add features should be minor and those that fix bugs should be patch. Please prefix changeset messages with feat:, fix:, or chore:.

Edits

  • Please ensure that 'Allow edits from maintainers' is checked. PRs without this option may be closed.

@pkg-svelte-dev

pkg-svelte-dev Bot commented Jul 16, 2026

Copy link
Copy Markdown

Install the latest version of @sveltejs/kit from 294a1fd:

pnpm add https://pkg.svelte.dev/@sveltejs/kit/c/294a1fd1faa209f842b76f5933b31a2b78e55a6a

Open in pkg.svelte.dev: https://pkg.svelte.dev/repos/kit/pr/16372

@changeset-bot

changeset-bot Bot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 294a1fd

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@sveltejs/kit Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@svelte-docs-bot

Copy link
Copy Markdown

Comment thread .changeset/app-manifest-module.md Outdated
@Rich-Harris

Copy link
Copy Markdown
Member Author

Renamed build to immutable and files to assets. Couple of wrinkles:

  • immutable contains a non-immutable file, namely _app/manifest.js itself. Which is weird. I guess we could move it to assets, though that would also be weird, but perhaps less weird.
  • immutable/asset path strings no longer have the leading / (and nor does the Asset type) which I think makes sense. But I'm not sure what makes most sense for prerendered path strings. You wouldn't use it with asset(...), you'd use it with resolve(...), if anything, and resolve does expect a leading slash.

I wonder if we actually need to remove the leading / from RouteId as well. I can see that making sense — when you use a route ID you're resolving against the base path, and resolve(anything, '/foo') should, by rights, equal /foo`. Slightly disruptive change but maybe for the best.

@Rich-Harris

Copy link
Copy Markdown
Member Author

Actually you know what? This could be how we distinguish route IDs from pathnames in resolve. This makes way more sense to me:

// valid
resolve('path/to/somewhere');
resolve('/path/to/[location]', { location: 'somewhere' });

// invalid
resolve('/path/to/somewhere');
resolve('path/to/[location]', { location: 'somewhere' });

@Rich-Harris

Copy link
Copy Markdown
Member Author

Should probably also rename Pathname to Path, since Pathname suggests url.pathname which this isn't. Also it aligns with the path properties in the manifest

Comment thread packages/kit/src/types/ambient.d.ts Outdated
Comment thread packages/kit/src/types/ambient.d.ts Outdated
Comment thread packages/kit/src/types/ambient.d.ts Outdated
@Rich-Harris
Rich-Harris marked this pull request as ready for review July 20, 2026 00:49
Rich-Harris added a commit that referenced this pull request Jul 20, 2026
This PR allows you to do this inside service workers:

```js
import { version } from '$app/env';
```

This is useful for generating verison-scoped caches and suchlike.
Together with #16372 it brings us closer to resolving #16159 — the only
thing we would still need from the `$service-worker` module is a
replacement for `base`, which means getting `$app/paths` to work in a
service worker context.

---

### Please don't delete this checklist! Before submitting the PR, please
make sure you do the following:
- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] This message body should clearly illustrate what problems it
solves.
- [x] Ideally, include a test that fails without this PR but passes with
it.

### Tests
- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint` and `pnpm check`

### Changesets
- [x] If your PR makes a change that should be noted in one or more
packages' changelogs, generate a changeset by running `pnpm changeset`
and following the prompts. Changesets that add features should be
`minor` and those that fix bugs should be `patch`. Please prefix
changeset messages with `feat:`, `fix:`, or `chore:`.

### Edits

- [x] Please ensure that 'Allow edits from maintainers' is checked. PRs
without this option may be closed.

---------

Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Comment thread packages/kit/src/exports/vite/index.js
format: inline ? 'iife' : 'esm',
entryFileNames: `${app_immutable}/[name].[hash].js`,
chunkFileNames: `${app_immutable}/chunks/[hash].js`,
chunkFileNames: (/** @type {Rolldown.PreRenderedChunk} */ chunk_info) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Random thought: Do we have tests asserting that hashes are stable?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread packages/kit/src/exports/vite/index.js Outdated
Rich-Harris added a commit that referenced this pull request Jul 20, 2026
While working on #16372 I had a realisation that we shouldn't prefix
`Asset` and `Pathname` strings with a `/`. It makes no sense — these are
intended to be relative to the base path, and so having them be
root-relative (`/favicon.png` instead of `favicon.png`) is wrong.

This change means `Pathname` is the wrong name, since it wrongly implies
a root-relative `url.pathname`. As well as removing the leading slash
this PR renames `Pathname` to `Path`, and `Asset` to `AssetPath`. (This
allows future us to add a new `Asset` type containing `path: AssetPath`,
and possibly other stuff like `type` and `size`.)

There are practical benefits to removing the leading slash:

- In #16372, we're adding an `$app/manifest` module with `immutable` and
`assets` exports. The latter is an array of objects with a `path`
property — rather than typing it as `string`, we can type it as
`AssetPath`. But if it had a leading slash then it would be unsuitable
for its main purpose, which is for use with `cache.add(path)` inside a
service worker. We can either make it base-path-relative (as this PR
proposes) or root-relative (i.e. `/my-basepath/favicon.png`) which is
inherently less portable.
- Similarly, we can type the `path` elements in the `prerendered` array
as `Path` rather than `string`
- We can more easily distinguish between `resolve(path)` and
`resolve(route_id)`, both type-wise and at runtime. At the moment
they're sort of smushed together awkwardly

I also think that `asset('favicon.png')` is just nicer to look at than
`asset('/favicon.png')` which is either weird or pointless depending on
whether there's a configured `base`.

Anyway: this is all somewhat independent of the `$app/manifest` stuff so
I figured it deserved its own PR.

Closes #16425, by tightening up the type of `AssetPath` (no `string &
{}`).

---

### Please don't delete this checklist! Before submitting the PR, please
make sure you do the following:
- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] This message body should clearly illustrate what problems it
solves.
- [x] Ideally, include a test that fails without this PR but passes with
it.

### Tests
- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint` and `pnpm check`

### Changesets
- [x] If your PR makes a change that should be noted in one or more
packages' changelogs, generate a changeset by running `pnpm changeset`
and following the prompts. Changesets that add features should be
`minor` and those that fix bugs should be `patch`. Please prefix
changeset messages with `feat:`, `fix:`, or `chore:`.

### Edits

- [x] Please ensure that 'Allow edits from maintainers' is checked. PRs
without this option may be closed.

---------

Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: Tee Ming <chewteeming01@gmail.com>
@Rich-Harris
Rich-Harris merged commit adc4c5b into version-3 Jul 20, 2026
20 checks passed
@Rich-Harris
Rich-Harris deleted the app-manifest branch July 20, 2026 18:22
Rich-Harris pushed a commit that referenced this pull request Jul 24, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to version-3, this PR
will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

`version-3` is currently in **pre mode** so this branch has prereleases
rather than normal releases. If you want to exit prereleases, run
`changeset pre exit` on `version-3`.

⚠️⚠️⚠️⚠️⚠️⚠️

# Releases
## @sveltejs/kit@3.0.0-next.12

### Major Changes

- breaking: rename `Pathname` type to `Path` and `Asset` to `AssetPath`
([#16430](#16430))
  breaking: remove leading `/` from `Path` and `AssetPath`
- breaking: write tsconfig to `node_modules/$app/tsconfig`
([#16458](#16458))

- breaking: error on `event.url`, `event.params` and `event.route`
access inside queries
([#16452](#16452))

- breaking: delete `$service-worker` module
([#16450](#16450))

- breaking: detect new deployments on data, remote, and form action
responses, tab focus, and visibility change, and default
`version.pollInterval` to 1 hour
([#16496](#16496))

### Minor Changes

- feat: add `$app/manifest` module with `immutable`, `assets`,
`prerendered`, and `routes` exports
([#16372](#16372))

- feat: validate that all remote form fields were created with
form.fields.foo.as(...)
([#16331](#16331))

- feat: make `$app/paths` importable in service workers
([#16441](#16441))

- feat: `$app/service-worker` module
([#16458](#16458))

- feat: better tsconfig validation
([#16458](#16458))

- fix: default cookies to `secure` to `false` during development
([#16462](#16462))

### Patch Changes

- fix: allow `undefined` values to be passed to form field `.as(...)`
where applicable ([#15681](#15681))

- fix: include queries refreshed from within another query in the
serialized response
([#16461](#16461))

- fix: generate sourcemaps for remote modules
([#16440](#16440))

- fix: avoid empty getElementById() call on hash routing navigation
([#16448](#16448))

- fix: warn if hook files are spelled as "hook" instead of "hooks"
([#16483](#16483))

- chore: deprecate the `alias` option
([#16470](#16470))

- fix: prevent infinite loops when server-side queries refresh each
other in a cycle during the single-flight drain
([#16461](#16461))

- fix: populate `version` in service workers
([#16434](#16434))

- fix: resolve remote modules as external during dev prebundling so
packages can re-export remote functions
([#16426](#16426))

- fix: refetch route-tracking server data when navigating away from an
error page ([#16381](#16381))

- fix: serialize `query(...).set(...)`/`query(...).refresh()` values
into the rendered HTML when called from within a query during SSR
([#16461](#16461))

- fix: fall back to the page's form actions when a sibling endpoint has
no POST handler ([#16349](#16349))

- fix: return a lightweight 404 instead of rendering the error page for
subresource requests
([#16463](#16463))

- fix: preserve stripped path prefixes by making trailing-slash
redirects relative
([#16431](#16431))

- chore: deduplicate type-stripping logic in `tweak_types`
([#16454](#16454))

- fix: more informative error message when running a command inside a
query or prerender function
([`eb5c973`](eb5c973))
## @sveltejs/adapter-cloudflare@8.0.0-next.3

### Patch Changes

- chore: bump `@cloudflare/workers-types` to `4.20260621.1`
([#16455](#16455))
- Updated dependencies
[[`adc4c5b`](adc4c5b),
[`ecb0701`](ecb0701),
[`4b4cc60`](4b4cc60),
[`7390dbe`](7390dbe),
[`2e71340`](2e71340),
[`c87bc3a`](c87bc3a),
[`af15c6c`](af15c6c),
[`c6fa431`](c6fa431),
[`bfe4dea`](bfe4dea),
[`df7dc72`](df7dc72),
[`5ae11a1`](5ae11a1),
[`781205c`](781205c),
[`c87bc3a`](c87bc3a),
[`a1bfeb9`](a1bfeb9),
[`c87bc3a`](c87bc3a),
[`9f0127d`](9f0127d),
[`4b4cc60`](4b4cc60),
[`8f5b9c7`](8f5b9c7),
[`c9b5544`](c9b5544),
[`fe1d4a4`](fe1d4a4),
[`4b4cc60`](4b4cc60),
[`5f78e95`](5f78e95),
[`25510fc`](25510fc),
[`c99a6cf`](c99a6cf),
[`17a45ca`](17a45ca),
[`2ca20c3`](2ca20c3),
[`eb5c973`](eb5c973)]:
  - @sveltejs/kit@3.0.0-next.12
## @sveltejs/adapter-netlify@7.0.0-next.4

### Patch Changes

- fix: await `init` on every request to prevent race condition
([#16467](#16467))

- chore: bump Rolldown to `1.2.0`
([#16455](#16455))
- Updated dependencies
[[`adc4c5b`](adc4c5b),
[`ecb0701`](ecb0701),
[`4b4cc60`](4b4cc60),
[`7390dbe`](7390dbe),
[`2e71340`](2e71340),
[`c87bc3a`](c87bc3a),
[`af15c6c`](af15c6c),
[`c6fa431`](c6fa431),
[`bfe4dea`](bfe4dea),
[`df7dc72`](df7dc72),
[`5ae11a1`](5ae11a1),
[`781205c`](781205c),
[`c87bc3a`](c87bc3a),
[`a1bfeb9`](a1bfeb9),
[`c87bc3a`](c87bc3a),
[`9f0127d`](9f0127d),
[`4b4cc60`](4b4cc60),
[`8f5b9c7`](8f5b9c7),
[`c9b5544`](c9b5544),
[`fe1d4a4`](fe1d4a4),
[`4b4cc60`](4b4cc60),
[`5f78e95`](5f78e95),
[`25510fc`](25510fc),
[`c99a6cf`](c99a6cf),
[`17a45ca`](17a45ca),
[`2ca20c3`](2ca20c3),
[`eb5c973`](eb5c973)]:
  - @sveltejs/kit@3.0.0-next.12
## @sveltejs/adapter-node@6.0.0-next.6

### Patch Changes

- fix: preserve stripped path prefixes by making trailing-slash
redirects relative
([#16431](#16431))

- chore: bump Rolldown to `1.2.0`
([#16455](#16455))
- Updated dependencies
[[`adc4c5b`](adc4c5b),
[`ecb0701`](ecb0701),
[`4b4cc60`](4b4cc60),
[`7390dbe`](7390dbe),
[`2e71340`](2e71340),
[`c87bc3a`](c87bc3a),
[`af15c6c`](af15c6c),
[`c6fa431`](c6fa431),
[`bfe4dea`](bfe4dea),
[`df7dc72`](df7dc72),
[`5ae11a1`](5ae11a1),
[`781205c`](781205c),
[`c87bc3a`](c87bc3a),
[`a1bfeb9`](a1bfeb9),
[`c87bc3a`](c87bc3a),
[`9f0127d`](9f0127d),
[`4b4cc60`](4b4cc60),
[`8f5b9c7`](8f5b9c7),
[`c9b5544`](c9b5544),
[`fe1d4a4`](fe1d4a4),
[`4b4cc60`](4b4cc60),
[`5f78e95`](5f78e95),
[`25510fc`](25510fc),
[`c99a6cf`](c99a6cf),
[`17a45ca`](17a45ca),
[`2ca20c3`](2ca20c3),
[`eb5c973`](eb5c973)]:
  - @sveltejs/kit@3.0.0-next.12
## @sveltejs/adapter-vercel@7.0.0-next.3

### Patch Changes

- fix: await `init` on every request to prevent race condition
([#16467](#16467))

- chore: bump Rolldown to `1.2.0`
([#16455](#16455))
- Updated dependencies
[[`adc4c5b`](adc4c5b),
[`ecb0701`](ecb0701),
[`4b4cc60`](4b4cc60),
[`7390dbe`](7390dbe),
[`2e71340`](2e71340),
[`c87bc3a`](c87bc3a),
[`af15c6c`](af15c6c),
[`c6fa431`](c6fa431),
[`bfe4dea`](bfe4dea),
[`df7dc72`](df7dc72),
[`5ae11a1`](5ae11a1),
[`781205c`](781205c),
[`c87bc3a`](c87bc3a),
[`a1bfeb9`](a1bfeb9),
[`c87bc3a`](c87bc3a),
[`9f0127d`](9f0127d),
[`4b4cc60`](4b4cc60),
[`8f5b9c7`](8f5b9c7),
[`c9b5544`](c9b5544),
[`fe1d4a4`](fe1d4a4),
[`4b4cc60`](4b4cc60),
[`5f78e95`](5f78e95),
[`25510fc`](25510fc),
[`c99a6cf`](c99a6cf),
[`17a45ca`](17a45ca),
[`2ca20c3`](2ca20c3),
[`eb5c973`](eb5c973)]:
  - @sveltejs/kit@3.0.0-next.12

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Rich-Harris added a commit that referenced this pull request Jul 29, 2026
…ient's (#16531)

#16372 added the `_app/immutable` filter when computing
`$app/manifest`'s `immutable`, but the serviceWorker environment kept
the unfiltered file set it inherited from `$service-worker`'s `build`.
That copy includes `_app/manifest.js`, whose filename never changes, so
a service worker that caches `immutable` per the docs serves the
previous deployment's manifest after a redeploy. Both lists are now
computed by one helper; the `bundleStrategy: 'inline'` special case is
subsumed by its existence check.

The truthiness checks on `css`/`assets` make the helper robust to the
full range of Vite's optional `css?: string[]` field, so a hypothetical
`css: null` entry is skipped cleanly instead of crashing the build.

The client-side list was also computed before the `bundleStrategy:
'inline'` deletions it was supposed to reflect, so the page's
`immutable` included the deleted bundle and stylesheet while the service
worker's, computed after them, did not. The inlined files are now
tracked directly and excluded everywhere, instead of checking the
filesystem after the fact.

`_app/manifest.js` is no longer precached, so an offline-first app that
imports `$app/manifest` in client code only has it cached once a page
has fetched it. The precached copy was guaranteed stale after the next
deploy anyway; moving the chunk to `assets`, as floated in #16372, would
give such apps a correct offline copy.

---------

Co-authored-by: Rich Harris <richard.a.harris@gmail.com>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
@teemingc teemingc linked an issue Jul 30, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Programmatically get all routes

3 participants