diff --git a/.changeset/brown-pens-glow.md b/.changeset/brown-pens-glow.md new file mode 100644 index 000000000000..28e03c552db3 --- /dev/null +++ b/.changeset/brown-pens-glow.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[breaking] Overhaul filesystem-based router (https://github.com/sveltejs/kit/discussions/5774) diff --git a/.changeset/honest-pandas-joke.md b/.changeset/honest-pandas-joke.md new file mode 100644 index 000000000000..ed73b4246d93 --- /dev/null +++ b/.changeset/honest-pandas-joke.md @@ -0,0 +1,5 @@ +--- +'create-svelte': patch +--- + +Update templates diff --git a/.changeset/orange-ties-tell.md b/.changeset/orange-ties-tell.md new file mode 100644 index 000000000000..dbe12ea3e2b9 --- /dev/null +++ b/.changeset/orange-ties-tell.md @@ -0,0 +1,5 @@ +--- +'svelte-migrate': patch +--- + +Rewrite type names diff --git a/.changeset/thin-carrots-sin.md b/.changeset/thin-carrots-sin.md new file mode 100644 index 000000000000..dae729f07fea --- /dev/null +++ b/.changeset/thin-carrots-sin.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[breaking] Change load API (https://github.com/sveltejs/kit/discussions/5774) diff --git a/documentation/docs/01-project-structure.md b/documentation/docs/01-project-structure.md index f3e22acf84f7..dff7f3d218be 100644 --- a/documentation/docs/01-project-structure.md +++ b/documentation/docs/01-project-structure.md @@ -33,7 +33,7 @@ The `src` directory contains the meat of your project. - `lib` contains your library code, which can be imported via the [`$lib`](/docs/modules#$lib) alias, or packaged up for distribution using [`svelte-kit package`](/docs/packaging) - `params` contains any [param matchers](/docs/routing#advanced-routing-matching) your app needs -- `routes` contains the [pages](/docs/routing#pages) and [endpoints](/docs/routing#endpoints) of your application +- `routes` contains the [routes](/docs/routing) of your application - `app.html` is your page template — an HTML document containing the following placeholders: - `%sveltekit.head%` — `` and ` -A route can have multiple dynamic parameters, for example `src/routes/[category]/[item].svelte` or even `src/routes/[category]-[item].svelte`. (Parameters are 'non-greedy'; in an ambiguous case like `x-y-z`, `category` would be `x` and `item` would be `y-z`.) +

{data.title}

+
{@html data.content}
+``` -### Endpoints +> Note that SvelteKit uses `` elements to navigate between routes, rather than a framework-specific `` component. -Endpoints are modules written in `.js` (or `.ts`) files that export [request handler](/docs/types#sveltejs-kit-requesthandler) functions corresponding to HTTP methods. Request handlers make it possible to read and write data that is only available on the server (for example in a database, or on the filesystem). +#### +page.js -Their job is to return a `{ status, headers, body }` object representing the response. +Often, a page will need to load some data before it can be rendered. For this, we add a `+page.js` (or `+page.ts`, if you're TypeScript-inclined) module that exports a `load` function: ```js -/// file: src/routes/random.js -/** @type {import('@sveltejs/kit').RequestHandler} */ -export async function GET() { - return { - status: 200, - headers: { - 'access-control-allow-origin': '*' - }, - body: { - number: Math.random() - } - }; -} -``` +/// file: src/routes/blog/[slug]/+page.js +import { error } from '@sveltejs/kit'; -- `status` is an [HTTP status code](https://httpstatusdogs.com): - - `2xx` — successful response (default is `200`) - - `3xx` — redirection (should be accompanied by a `location` header) - - `4xx` — client error - - `5xx` — server error -- `headers` can either be a plain object, as above, or an instance of the [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) class -- `body` can be a plain object or, if something goes wrong, an [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error). It will be serialized as JSON - -A `GET` or `HEAD` response must include a `body`, but beyond that restriction all three properties are optional. +/** @type {import('./$types').PageLoad} */ +export function load({ params }) { + if (params.slug === 'hello-world') { + return { + title: 'Hello world!', + content: 'Welcome to our blog. Lorem ipsum dolor sit amet...' + }; + } -#### Page endpoints + throw error(404, 'Not found'); +} +``` -If an endpoint has the same filename as a page (except for the extension), the page gets its props from the endpoint — via `fetch` during client-side navigation, or via direct function call during SSR. (If a page uses syntax for [named layouts](/docs/layouts#named-layouts) or [matchers](/docs/routing#advanced-routing-matching) in its filename then the corresponding page endpoint's filename must also include them.) +This function runs alongside `+page.svelte`, which means it runs on the server during server-side rendering and in the browser during client-side navigation. See [`load`](/docs/load) for full details of the API. -For example, you might have a `src/routes/items/[id].svelte` page... +As well as `load`, `page.js` can export values that configure the page's behaviour: -```svelte -/// file: src/routes/items/[id].svelte - +- `export const prerender = true` or `false` overrides [`config.kit.prerender.default`](/docs/configuration#prerender) +- `export const hydrate = true` or `false` overrides [`config.kit.browser.hydrate`](/docs/configuration#browser) +- `export const router = true` or `false` overrides [`config.kit.browser.router`](/docs/configuration#browser) -

{item.title}

-``` +#### +page.server.js -...paired with a `src/routes/items/[id].js` endpoint (don't worry about the `$lib` import, we'll get to that [later](/docs/modules#$lib)): +If your `load` function can only run on the server — for example, if it needs to fetch data from a database or you need to access private [environment variables](/docs/modules#$env-static-private) like API keys — then you can rename `+page.js` to `+page.server.js` and change the `PageLoad` type to `PageServerLoad`. ```js -/// file: src/routes/items/[id].js -// @filename: ambient.d.ts -type Item = {}; +/// file: src/routes/blog/[slug]/+page.server.js -declare module '$lib/database' { - export const get: (id: string) => Promise; +// @filename: ambient.d.ts +declare global { + const getPostFromDatabase: (slug: string) => { + title: string; + content: string; + } } -// @filename: __types/[id].d.ts -import type { RequestHandler as GenericRequestHandler } from '@sveltejs/kit'; -export type RequestHandler = GenericRequestHandler<{ id: string }, Body>; +export {}; // @filename: index.js // ---cut--- -import db from '$lib/database'; +import { error } from '@sveltejs/kit'; -/** @type {import('./__types/[id]').RequestHandler} */ -export async function GET({ params }) { - // `params.id` comes from [id].js - const item = await db.get(params.id); +/** @type {import('./$types').PageServerLoad} */ +export async function load({ params }) { + const post = await getPostFromDatabase(params.slug); - if (item) { - return { - status: 200, - headers: {}, - body: { item } - }; + if (post) { + return post; } - return { - status: 404 - }; + throw error(404, 'Not found'); } ``` -> The type of the `GET` function above comes from `./__types/[id].d.ts`, which is a file generated by SvelteKit (inside your [`outDir`](/docs/configuration#outdir), using the [`rootDirs`](https://www.typescriptlang.org/tsconfig#rootDirs) option) that provides type safety when accessing `params`. See the section on [generated types](/docs/types#generated-types) for more detail. - -To get the raw data instead of the page, you can include an `accept: application/json` header in the request, or — for convenience — append `/__data.json` to the URL, e.g. `/items/[id]/__data.json`. - -#### Standalone endpoints +During client-side navigation, SvelteKit will load this data using `fetch`, which means that the returned value must be serializable as JSON. -Most commonly, endpoints exist to provide data to the page with which they're paired. They can, however, exist separately from pages. Standalone endpoints have slightly more flexibility over the returned `body` type — in addition to objects and [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) instances, they can return a [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) or a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream). +#### Actions -Standalone endpoints can be given a file extension if desired, or accessed directly if not: +`+page.server.js` can also declare _actions_, which correspond to the `POST`, `PATCH`, `PUT` and `DELETE` HTTP methods. A request made to the page with one of these methods will invoke the corresponding action before rendering the page. -| filename | endpoint | -| ----------------------------- | ---------- | -| src/routes/data/index.json.js | /data.json | -| src/routes/data.json.js | /data.json | -| src/routes/data/index.js | /data | -| src/routes/data.js | /data | - -#### POST, PUT, PATCH, DELETE - -Endpoints can handle any HTTP method — not just `GET` — by exporting the corresponding function: +An action can return a `{ status?, errors }` object if there are validation errors (`status` defaults to `400`), or an optional `{ location }` object to redirect the user to another page: ```js -// @noErrors -export function POST(event) {...} -export function PUT(event) {...} -export function PATCH(event) {...} -export function DELETE(event) {...} -``` +/// file: src/routes/login/+page.server.js -These functions can, like `GET`, return a `body` that will be passed to the page as props. Whereas 4xx/5xx responses from `GET` will result in an error page rendering, similar responses to non-GET requests do not, allowing you to do things like render form validation errors: - -```js -/// file: src/routes/items.js // @filename: ambient.d.ts -type Item = { - id: string; -}; -type ValidationError = {}; - -declare module '$lib/database' { - export const list: () => Promise; - export const create: (request: Request) => Promise<[Record, Item]>; +declare global { + const createSessionCookie: (userid: string) => string; + const hash: (content: string) => string; + const db: { + findUser: (name: string) => Promise<{ + id: string; + username: string; + password: string; + }> + } } -// @filename: __types/items.d.ts -import type { RequestHandler as GenericRequestHandler } from '@sveltejs/kit'; -export type RequestHandler = GenericRequestHandler<{}, Body>; +export {}; // @filename: index.js // ---cut--- -import * as db from '$lib/database'; +import { error } from '@sveltejs/kit'; -/** @type {import('./__types/items').RequestHandler} */ -export async function GET() { - const items = await db.list(); +/** @type {import('./$types').Action} */ +export async function POST({ request, setHeaders, url }) { + const values = await request.formData(); - return { - body: { items } - }; -} + const username = /** @type {string} */ (values.get('username')); + const password = /** @type {string} */ (values.get('password')); -/** @type {import('./__types/items').RequestHandler} */ -export async function POST({ request }) { - const [errors, item] = await db.create(request); + const user = await db.findUser(username); - if (errors) { - // return validation errors + if (!user) { return { - status: 400, - body: { errors } + status: 403, + errors: { + username: 'No user with this username' + } }; } - // redirect to the newly created item + if (user.password !== hash(password)) { + return { + status: 403, + errors: { + password: 'Incorrect password' + } + }; + } + + setHeaders({ + 'set-cookie': createSessionCookie(user.id) + }); + return { - status: 303, - headers: { - location: `/items/${item.id}` - } + location: url.searchParams.get('redirectTo') ?? '/' }; } ``` -```svelte -/// file: src/routes/items.svelte - +> The actions API will likely change in the near future: https://github.com/sveltejs/kit/discussions/5875 -{#each items as item} - -{/each} +### +error -
- +If an error occurs during `load`, SvelteKit will render a default error page. You can customise this error page on a per-route basis by adding an `+error.svelte` file: - {#if errors?.title} -

{errors.title}

- {/if} +```svelte +/// file: src/routes/blog/[slug]/+error.svelte + - -
+

{$page.status}: {$page.error.message}

``` -#### Body parsing +SvelteKit will 'walk up the tree' looking for the closest error boundary — if the file above didn't exist it would try `src/routes/blog/+error.svelte` and `src/routes/+error.svelte` before rendering the default error page. -The `request` object is an instance of the standard [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) class. As such, accessing the request body is easy: +### +layout -```js -// @filename: ambient.d.ts -declare global { - const create: (data: any) => any; -} +So far, we've treated pages as entirely standalone components — upon navigation, the existing `+page.svelte` component will be destroyed, and a new one will take its place. -export {}; +But in many apps, there are elements that should be visible on _every_ page, such as top-level navigation or a footer. Instead of repeating them in every `+page.svelte`, we can put them in _layouts_. -// @filename: index.js -// ---cut--- -/** @type {import('@sveltejs/kit').RequestHandler} */ -export async function POST({ request }) { - const data = await request.formData(); // or .json(), or .text(), etc +#### +layout.svelte - await create(data); - return { status: 201 }; -} -``` - -#### Setting cookies +To create a layout that applies to every page, make a file called `src/routes/+layout.svelte`. The default layout (the one that SvelteKit uses if you don't bring your own) looks like this... -Endpoints can set cookies by returning a `headers` object with `set-cookie`. To set multiple cookies simultaneously, return an array: +```html + +``` -```js -// @filename: ambient.d.ts -const cookie1: string; -const cookie2: string; +...but we can add whatever markup, styles and behaviour we want. The only requirement is that the component includes a `` for the page content. For example, let's add a nav bar: -// @filename: index.js -// ---cut--- -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return { - headers: { - 'set-cookie': [cookie1, cookie2] - } - }; -} +```html +/// file: src/routes/+layout.svelte +
+ + ``` -#### HTTP method overrides - -HTML `
` elements only support `GET` and `POST` methods natively. You can allow other methods, like `PUT` and `DELETE`, by specifying them in your [configuration](/docs/configuration#methodoverride) and adding a `_method=VERB` parameter (you can configure the name) to the form's `action`: +If we create pages for `/`, `/about` and `/settings`... -```js -/// file: svelte.config.js -/** @type {import('@sveltejs/kit').Config} */ -const config = { - kit: { - methodOverride: { - allowed: ['PUT', 'PATCH', 'DELETE'] - } - } -}; +```html +/// file: src/routes/+page.svelte +

Home

+``` -export default config; +```html +/// file: src/routes/about/+page.svelte +

About

``` ```html - - -
+/// file: src/routes/settings/+page.svelte +

Settings

``` -> Using native `
` behaviour ensures your app continues to work when JavaScript fails or is disabled. +...the nav will always be visible, and clicking between the three pages will only result in the `

` being replaced. -### Private modules +Layouts can be _nested_. Suppose we don't just have a single `/settings` page, but instead have nested pages like `/settings/profile` and `/settings/notifications` with a shared submenu (for a real-life example, see [github.com/settings](https://github.com/settings)). -Files and directories with a leading `_` or `.` (other than [`.well-known`](https://en.wikipedia.org/wiki/Well-known_URI)) are private by default, meaning that they do not create routes (but can be imported by files that do). You can configure which modules are considered public or private with the [`routes`](/docs/configuration#routes) configuration. +We can create a layout that only applies to pages below `/settings` (while inheriting the root layout with the top-level nav): -### Advanced routing +```svelte +/// file: src/routes/settings/+layout.svelte + -#### Rest parameters +

Settings

-If the number of route segments is unknown, you can use rest syntax — for example you might implement GitHub's file viewer like so... + -```bash -/[org]/[repo]/tree/[branch]/[...file] + ``` -...in which case a request for `/sveltejs/kit/tree/master/documentation/docs/01-routing.md` would result in the following parameters being available to the page: +#### +layout.js + +Just like `+page.svelte` loading data from `+page.js`, your `+layout.svelte` component can get data from a [`load`](/docs/load) function in `+layout.js`. ```js -// @noErrors -{ - org: 'sveltejs', - repo: 'kit', - branch: 'master', - file: 'documentation/docs/01-routing.md' +/// file: src/routes/settings/+layout.js +/** @type {import('./$types').LayoutLoad} */ +export function load() { + return { + sections: [ + { slug: 'profile', title: 'Profile' }, + { slug: 'notifications', title: 'Notifications' } + ] + }; } ``` -> `src/routes/a/[...rest]/z.svelte` will match `/a/z` (i.e. there's no parameter at all) as well as `/a/b/z` and `/a/b/c/z` and so on. Make sure you check that the value of the rest parameter is valid, for example using a [matcher](#advanced-routing-matching). +Unlike `+page.js`, `+layout.js` cannot export `prerender`, `hydrate` and `router`, as these are page-level options. -#### Matching +> Often, layout data is unchanged when navigating between pages. SvelteKit will intelligently re-run [`load`](/docs/load) functions when necessary. -A route like `src/routes/archive/[page]` would match `/archive/3`, but it would also match `/archive/potato`. We don't want that. You can ensure that route parameters are well-formed by adding a _matcher_ — which takes the parameter string (`"3"` or `"potato"`) and returns `true` if it is valid — to your [`params`](/docs/configuration#files) directory... +#### +layout.server.js -```js -/// file: src/params/integer.js -/** @type {import('@sveltejs/kit').ParamMatcher} */ -export function match(param) { - return /^\d+$/.test(param); -} -``` - -...and augmenting your routes: - -```diff --src/routes/archive/[page] -+src/routes/archive/[page=integer] -``` +To run your layout's `load` function on the server, move it to `+layout.server.js`, and change the `LayoutLoad` type to `LayoutServerLoad`. -If the pathname doesn't match, SvelteKit will try to match other routes (using the sort order specified below), before eventually returning a 404. +### +server -> Matchers run both on the server and in the browser. +As well as pages, you can define routes with a `+server.js` file (sometimes referred to as an 'API route' or an 'endpoint'), which gives you full control over the response. Your `+server.js` file (or `+server.ts`) exports functions corresponding to HTTP verbs like `GET`, `POST`, `PATCH`, `PUT` and `DELETE` that take a `RequestEvent` argument and return a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object. -#### Sorting +For example we could create an `/api/random-number` route with a `GET` handler: -It's possible for multiple routes to match a given path. For example each of these routes would match `/foo-abc`: +```js +/// file: src/routes/api/random-number/+server.js +import { error } from '@sveltejs/kit'; -```bash -src/routes/[...catchall].svelte -src/routes/[a].js -src/routes/[b].svelte -src/routes/foo-[c].svelte -src/routes/foo-abc.svelte -``` +/** @type {import('./$types').RequestHandler} */ +export function GET({ url }) { + const min = Number(url.searchParams.get('min') ?? '0'); + const max = Number(url.searchParams.get('max') ?? '1'); -SvelteKit needs to know which route is being requested. To do so, it sorts them according to the following rules... + const d = max - min; -- More specific routes are higher priority (e.g. a route with no parameters is more specific than a route with one dynamic parameter, and so on) -- Standalone endpoints have higher priority than pages with the same specificity -- Parameters with [matchers](#advanced-routing-matching) (`[name=type]`) are higher priority than those without (`[name]`) -- Rest parameters have lowest priority -- Ties are resolved alphabetically + if (isNaN(d) || d < 0) { + throw error(400, 'min and max must be numbers, and min must be less than max'); + } -...resulting in this ordering, meaning that `/foo-abc` will invoke `src/routes/foo-abc.svelte`, and `/foo-def` will invoke `src/routes/foo-[c].svelte` rather than less specific routes: + const random = min + Math.random() * d; -```bash -src/routes/foo-abc.svelte -src/routes/foo-[c].svelte -src/routes/[a].js -src/routes/[b].svelte -src/routes/[...catchall].svelte + return new Response(String(random)); +} ``` -#### Encoding +The first argument to `Response` can be a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream), making it possible to stream large amounts of data or create server-sent events (unless deploying to platforms that buffer responses, like AWS Lambda). -Filenames are URI-decoded, meaning that (for example) a filename like `%40[username].svelte` would match characters beginning with `@`: +### $types -```js -// @filename: ambient.d.ts -declare global { - const assert: { - equal: (a: any, b: any) => boolean; - }; -} +Throughout the examples above, we've been importing types from a `$types.d.ts` file. This is a file SvelteKit creates for you in a hidden directory if you're using TypeScript (or JavaScript with JSDoc type annotations) to give you type safety when working with your root files. -export {}; +For example, annotating `export let data` with `PageData` (or `LayoutData`, for a `+layout.svelte` file) tells TypeScript that the type of `data` is whatever was returned from `load`: -// @filename: index.js -// ---cut--- -assert.equal( - decodeURIComponent('%40[username].svelte'), - '@[username].svelte' -); +```svelte +/// file: src/routes/blog/[slug]/+page.svelte + ``` -To express a `%` character, use `%25`, otherwise the result will be malformed. +In turn, annotating the `load` function with `PageLoad`, `PageServerLoad`, `LayoutLoad` or `LayoutServerLoad` (for `+page.js`, `+page.server.js`, `+layout.js` and `+layout.server.js` respectively) ensures that `params` and the return value are correctly typed. + +### Other files + +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`](/docs/modules#$lib). diff --git a/documentation/docs/04-advanced-routing.md b/documentation/docs/04-advanced-routing.md new file mode 100644 index 000000000000..4541b15f834f --- /dev/null +++ b/documentation/docs/04-advanced-routing.md @@ -0,0 +1,198 @@ +--- +title: Advanced routing +--- + +### Rest parameters + +If the number of route segments is unknown, you can use rest syntax — for example you might implement GitHub's file viewer like so... + +```bash +/[org]/[repo]/tree/[branch]/[...file] +``` + +...in which case a request for `/sveltejs/kit/tree/master/documentation/docs/04-advanced-routing.md` would result in the following parameters being available to the page: + +```js +// @noErrors +{ + org: 'sveltejs', + repo: 'kit', + branch: 'master', + file: 'documentation/docs/04-advanced-routing.md' +} +``` + +This also allows you to render custom 404s. Given these routes... + +``` +src/routes/ +├ marx-brothers/ +│ ├ chico/ +│ ├ harpo/ +│ ├ groucho/ +│ └ +error.svelte +└ +error.svelte +``` + +...the `marx-brothers/+error.svelte` file will _not_ be rendered if you visit `/marx-brothers/karl`, because no route was matched. If you want to render the nested error page, you should create a route that matches any `/marx-brothers/*` request, and return a 404 from it: + +```diff +src/routes/ +├ marx-brothers/ ++| ├ [...path]/ +│ ├ chico/ +│ ├ harpo/ +│ ├ groucho/ +│ └ +error.svelte +└ +error.svelte +``` + +> `src/routes/a/[...rest]/z/+page.svelte` will match `/a/z` (i.e. there's no parameter at all) as well as `/a/b/z` and `/a/b/c/z` and so on. Make sure you check that the value of the rest parameter is valid, for example using a [matcher](#advanced-routing-matching). + +### Matching + +A route like `src/routes/archive/[page]` would match `/archive/3`, but it would also match `/archive/potato`. We don't want that. You can ensure that route parameters are well-formed by adding a _matcher_ — which takes the parameter string (`"3"` or `"potato"`) and returns `true` if it is valid — to your [`params`](/docs/configuration#files) directory... + +```js +/// file: src/params/integer.js +/** @type {import('@sveltejs/kit').ParamMatcher} */ +export function match(param) { + return /^\d+$/.test(param); +} +``` + +...and augmenting your routes: + +```diff +-src/routes/archive/[page] ++src/routes/archive/[page=integer] +``` + +If the pathname doesn't match, SvelteKit will try to match other routes (using the sort order specified below), before eventually returning a 404. + +> Matchers run both on the server and in the browser. + +### Sorting + +It's possible for multiple routes to match a given path. For example each of these routes would match `/foo-abc`: + +```bash +src/routes/[...catchall]/+page.svelte +src/routes/[a]/+server.js +src/routes/[b]/+page.svelte +src/routes/foo-[c]/+page.svelte +src/routes/foo-abc/+page.svelte +``` + +SvelteKit needs to know which route is being requested. To do so, it sorts them according to the following rules... + +- More specific routes are higher priority (e.g. a route with no parameters is more specific than a route with one dynamic parameter, and so on) +- `+server` files have higher priority than `+page` files +- Parameters with [matchers](#advanced-routing-matching) (`[name=type]`) are higher priority than those without (`[name]`) +- Rest parameters have lowest priority +- Ties are resolved alphabetically + +...resulting in this ordering, meaning that `/foo-abc` will invoke `src/routes/foo-abc/+page.svelte`, and `/foo-def` will invoke `src/routes/foo-[c]/+page.svelte` rather than less specific routes: + +```bash +src/routes/foo-abc/+page.svelte +src/routes/foo-[c]/+page.svelte +src/routes/[a]/+server.js +src/routes/[b]/+page.svelte +src/routes/[...catchall]/+page.svelte +``` + +### Encoding + +Directory names are URI-decoded, meaning that (for example) a directory like `%40[username]` would match characters beginning with `@`: + +```js +// @filename: ambient.d.ts +declare global { + const assert: { + equal: (a: any, b: any) => boolean; + }; +} + +export {}; + +// @filename: index.js +// ---cut--- +assert.equal( + decodeURIComponent('%40[username]'), + '@[username]' +); +``` + +To express a `%` character, use `%25`, otherwise the result will be malformed. + +### Named layouts + +Some parts of your app might need something other than the default layout. For these cases you can create _named layouts_... + +```svelte +/// file: src/routes/+layout-foo.svelte +
+ +
+``` + +...and then use them by referencing the layout name (`foo`, in the example above) in the filename: + +```svelte +/// file: src/routes/my-special-page/+page@foo.svelte +

I am inside +layout-foo

+``` + +Named layouts are very powerful, but it can take a minute to get your head round them. Don't worry if this doesn't make sense all at once. + +#### Scoping + +Named layouts can be created at any depth, and will apply to any components in the same subtree. For example, `+layout-foo` will apply to `/x/one` and `/x/two`, but not `/x/three` or `/four`: + +```bash +src/routes/ +├ x/ +│ ├ +layout-foo.svelte +│ ├ one/+page@foo.svelte # ✅ page has `@foo` +│ ├ two/+page@foo.svelte # ✅ page has `@foo` +│ └ three/+page.svelte # ❌ page does not have `@foo` +└ four/+page@foo.svelte # ❌ page has `@foo`, but +layout-foo is not 'in scope' +``` + +#### Inheritance chains + +Layouts can themselves choose to inherit from named layouts, from the same directory or a parent directory. For example, `x/y/+layout@root.svelte` is the default layout for `/x/y` (meaning `/x/y/one`, `/x/y/two` and `/x/y/three` all inherit from it) because it has no name. Because it specifies `@root`, it will inherit directly from the nearest `+layout-root.svelte`, skipping `+layout.svelte` and `x/+layout.svelte`. + +``` +src/routes/ +├ x/ +│ ├ y/ +│ │ ├ +layout@root.svelte +│ │ ├ one/+page.svelte +│ │ ├ two/+page.svelte +│ │ └ three/+page.svelte +│ └ +layout.svelte +├ +layout.svelte +└ +layout-root.svelte +``` + +> In the case where `+layout-root.svelte` contains a lone ``, this effectively means we're able to 'reset' to a blank layout for any page or nested layout in the app by adding `@root`. + +If no parent is specified, a layout will inherit from the nearest default (i.e. unnamed) layout _above_ it in the tree. In some cases, it's helpful for a named layout to inherit from a default layout _alongside_ it in the tree, such as `+layout-root.svelte` inheriting from `+layout.svelte`. We can do this by explicitly specifying `@default`, allowing `/x/y/one` and siblings to use the app's default layout without using `x/+layout.svelte`: + +```diff +src/routes/ +├ x/ +│ ├ y/ +│ │ ├ +layout@root.svelte +│ │ ├ one/+page.svelte +│ │ ├ two/+page.svelte +│ │ └ three/+page.svelte +│ └ +layout.svelte +├ +layout.svelte +-└ +layout-root.svelte ++└ +layout-root@default.svelte +``` + +> `default` is a reserved name — in other words, you can't have a `+layout-default.svelte` file. diff --git a/documentation/docs/04-layouts.md b/documentation/docs/04-layouts.md deleted file mode 100644 index 1dcb2edd1a9a..000000000000 --- a/documentation/docs/04-layouts.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -title: Layouts ---- - -So far, we've treated pages as entirely standalone components — upon navigation, the existing component will be destroyed, and a new one will take its place. - -But in many apps, there are elements that should be visible on _every_ page, such as top-level navigation or a footer. Instead of repeating them in every page, we can use _layout_ components. - -To create a layout that applies to every page, make a file called `src/routes/__layout.svelte`. The default layout (the one that SvelteKit uses if you don't bring your own) looks like this... - -```html - -``` - -...but we can add whatever markup, styles and behaviour we want. The only requirement is that the component includes a `` for the page content. For example, let's add a nav bar: - -```html -/// file: src/routes/__layout.svelte - - - -``` - -If we create pages for `/`, `/about` and `/settings`... - -```html -/// file: src/routes/index.svelte -

Home

-``` - -```html -/// file: src/routes/about.svelte -

About

-``` - -```html -/// file: src/routes/settings.svelte -

Settings

-``` - -...the nav will always be visible, and clicking between the three pages will only result in the `

` being replaced. - -### Nested layouts - -Suppose we don't just have a single `/settings` page, but instead have nested pages like `/settings/profile` and `/settings/notifications` with a shared submenu (for a real-life example, see [github.com/settings](https://github.com/settings)). - -We can create a layout that only applies to pages below `/settings` (while inheriting the root layout with the top-level nav): - -```html -/// file: src/routes/settings/__layout.svelte -

Settings

- - - - -``` - -### Named layouts - -Some parts of your app might need something other than the default layout. For these cases you can create _named layouts_... - -```svelte -/// file: src/routes/__layout-foo.svelte -
- -
-``` - -...and then use them by referencing the layout name (`foo`, in the example above) in the filename: - -```svelte -/// file: src/routes/my-special-page@foo.svelte -

I am inside __layout-foo

-``` - -Named layouts are very powerful, but it can take a minute to get your head round them. Don't worry if this doesn't make sense all at once. - -#### Scoping - -Named layouts can be created at any depth, and will apply to any components in the same subtree. For example, `__layout-foo` will apply to `/x/one` and `/x/two`, but not `/x/three` or `/four`: - -```bash -src/routes/ -├ x/ -│ ├ __layout-foo.svelte -│ ├ one@foo.svelte # ✅ page has `@foo` -│ ├ two@foo.svelte # ✅ page has `@foo` -│ └ three.svelte # ❌ page does not have `@foo` -└ four@foo.svelte # ❌ page has `@foo`, but __layout-foo is not 'in scope' -``` - -#### Inheritance chains - -Layouts can themselves choose to inherit from named layouts, from the same directory or a parent directory. For example, `x/y/__layout@root.svelte` is the default layout for `/x/y` (meaning `/x/y/one`, `/x/y/two` and `/x/y/three` all inherit from it) because it has no name. Because it specifies `@root`, it will inherit directly from the nearest `__layout-root.svelte`, skipping `__layout.svelte` and `x/__layout.svelte`. - -``` -src/routes/ -├ x/ -│ ├ y/ -│ │ ├ __layout@root.svelte -│ │ ├ one.svelte -│ │ ├ two.svelte -│ │ └ three.svelte -│ └ __layout.svelte -├ __layout.svelte -└ __layout-root.svelte -``` - -> In the case where `__layout-root.svelte` contains a lone ``, this effectively means we're able to 'reset' to a blank layout for any page or nested layout in the app by adding `@root`. - -If no parent is specified, a layout will inherit from the nearest default (i.e. unnamed) layout _above_ it in the tree. In some cases, it's helpful for a named layout to inherit from a default layout _alongside_ it in the tree, such as `__layout-root.svelte` inheriting from `__layout.svelte`. We can do this by explicitly specifying `@default`, allowing `/x/y/one` and siblings to use the app's default layout without using `x/__layout.svelte`: - -```diff -src/routes/ -├ x/ -│ ├ y/ -│ │ ├ __layout@root.svelte -│ │ ├ one.svelte -│ │ ├ two.svelte -│ │ └ three.svelte -│ └ __layout.svelte -├ __layout.svelte --└ __layout-root.svelte -+└ __layout-root@default.svelte -``` - -> `default` is a reserved name — in other words, you can't have a `__layout-default.svelte` file. - -### Error pages - -If a page fails to load (see [Loading](/docs/loading)), SvelteKit will render an error page. You can customise this page by creating `__error.svelte` components alongside your layouts and pages. - -For example, if `src/routes/settings/notifications/index.svelte` failed to load, SvelteKit would render `src/routes/settings/notifications/__error.svelte` in the same layout, if it existed. If not, it would render `src/routes/settings/__error.svelte` in the parent layout, or `src/routes/__error.svelte` in the root layout. - -> SvelteKit provides a default error page in case you don't supply `src/routes/__error.svelte`, but it's recommended that you bring your own. - -If an error component has a [`load`](/docs/loading) function, it will be called with `error` and `status` properties: - -```html - - - - -

{title}

-``` - -> Layouts also have access to `error` and `status` via the [page store](/docs/modules#$app-stores) -> -> Server-side stack traces will be removed from `error` in production, to avoid exposing privileged information to users. - -#### 404s - -Nested error pages are only rendered when an error occurs while rendering a specific page. In the case of a request that doesn't match any existing route, SvelteKit will render a generic 404 instead. For example, given these routes... - -``` -src/routes/ -├ __error.svelte -├ marx-brothers/ -│ ├ __error.svelte -│ ├ chico.svelte -│ ├ harpo.svelte -│ └ groucho.svelte -``` - -...the `marx-brothers/__error.svelte` file will _not_ be rendered if you visit `/marx-brothers/karl`. If you want to render the nested error page, you should create a route that matches any `/marx-brothers/*` request, and return a 404 from it: - -```diff -src/routes/ -├ __error.svelte -├ marx-brothers/ -│ ├ __error.svelte -+│ ├ [...path].svelte -│ ├ chico.svelte -│ ├ harpo.svelte -│ └ groucho.svelte -``` - -```svelte -/// file: src/routes/marx-brothers/[...path].svelte - -``` diff --git a/documentation/docs/05-load.md b/documentation/docs/05-load.md new file mode 100644 index 000000000000..8631e21396a3 --- /dev/null +++ b/documentation/docs/05-load.md @@ -0,0 +1,334 @@ +--- +title: Loading data +--- + +A [`+page.svelte`](/docs/routing#page-page-svelte) or [`+layout.svelte`](/docs/routing#layout-layout-svelte) gets its `data` from a `load` function. + +If the `load` function is defined in `+page.js` or `+layout.js` it will run both on the server and in the browser. If it's instead defined in `+page.server.js` or `+layout.server.js` it will only run on the server, in which case it can (for example) make database calls and access private [environment variables](/docs/modules#$env-static-private), but can only return data that can be serialized as JSON. + +```js +/// file: src/routes/+page.js +/** @type {import('./$types').PageLoad} */ +export function load(event) { + return { + some: 'data' + }; +} +``` + +### Input properties + +The argument to a `load` function is a `LoadEvent` (or, for server-only `load` functions, a `ServerLoadEvent` which inherits `clientAddress`, `locals`, `platform` and `request` from `RequestEvent`). All events have the following properties: + +#### data + +Very rarely, you might need both a `+page.js` and a `+page.server.js` (or the `+layout` equivalent). In these cases, the `data` for `+page.svelte` comes from `+page.js`, which in turn receives `data` from the server: + +```js +/// file: src/routes/my-route/+page.server.js +/** @type {import('./$types').PageServerLoad} */ +export function load() { + return { + a: 1 + }; +} +``` + +```js +/// file: src/routes/my-route/+page.js +// @filename: $types.d.ts +export type PageLoad = import('@sveltejs/kit').Load<{}, { a: number }>; + +// @filename: index.js +// ---cut--- +/** @type {import('./$types').PageLoad} */ +export function load({ data }) { + return { + b: data.a * 2 + }; +} +``` + +```svelte +/// file: src/routes/my-route/+page.svelte + +``` + +In other words `+page.server.js` passes `data` along to `+page.js`, which passes `data` along to `+page.svelte`. + +#### params + +`params` is derived from `url.pathname` and the route filename. + +For a route filename example like `src/routes/a/[b]/[...c]` and a `url.pathname` of `/a/x/y/z`, the `params` object would look like this: + +```json +{ + "b": "x", + "c": "y/z" +} +``` + +#### routeId + +The name of the current route directory, relative to `src/routes`: + +```js +/// file: src/routes/blog/[slug]/+page.svelte +/** @type {import('./$types').PageLoad} */ +export function load({ routeId }) { + console.log(routeId); // 'blog/[slug]' +} +``` + +#### url + +An instance of [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL), containing properties like the `origin`, `hostname`, `pathname` and `searchParams` (which contains the parsed query string as a [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) object). `url.hash` cannot be accessed during `load`, since it is unavailable on the server. + +> In some environments this is derived from request headers during server-side rendering. If you're using [adapter-node](/docs/adapters#supported-environments-node-js), for example, you may need to configure the adapter in order for the URL to be correct. + +### Input methods + +`LoadEvent` also has the following methods: + +#### depends + +This function declares a _dependency_ on specific URLs, which can subsequently be used with [`invalidate()`](/docs/modules#$app-navigation-invalidate) to cause `load` to rerun. + +Most of the time you won't need this, as `fetch` calls `depends` on your behalf — it's only necessary if you're using a custom API client that bypasses `fetch`. + +URLs can be absolute or relative to the page being loaded, and must be [encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding). + +```js +// @filename: ambient.d.ts +declare module '$lib/api' { + interface Data{} + export const base: string; + export const client: { + get: (resource:string) => Promise + } +} + +// @filename: index.js +// ---cut--- +import * as api from '$lib/api'; + +/** @type {import('./$types').PageLoad} */ +export async function load({ depends }) { + depends(`${api.base}/foo`, `${api.base}/bar`); + + return { + foo: api.client.get('/foo'), + bar: api.client.get('/bar') + }; +} +``` + +#### fetch + +`fetch` is equivalent to the [native `fetch` web API](https://developer.mozilla.org/en-US/docs/Web/API/fetch), with a few additional features: + +- it can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request +- it can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context) +- internal requests (e.g. for `+server.js` routes) go direct to the handler function when running on the server, without the overhead of an HTTP call +- during server-side rendering, the response will be captured and inlined into the rendered HTML +- during hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request + +> Cookies will only be passed through if the target host is the same as the SvelteKit application or a more specific subdomain of it. + +#### parent + +`await parent()` returns data from parent layout `load` functions. In `+page.server.js` or `+layout.server.js` it will return data from `load` functions in parent `+layout.server.js` files: + +```js +/// file: src/routes/+layout.server.js +/** @type {import('./$types').LayoutLoad} */ +export function load() { + return { a: 1 }; +} +``` + +```js +/// file: src/routes/foo/+layout.server.js +// @filename: $types.d.ts +export type LayoutLoad = import('@sveltejs/kit').Load<{}, null, { a: number }>; + +// @filename: index.js +// ---cut--- +/** @type {import('./$types').LayoutLoad} */ +export async function load({ parent }) { + const { a } = await parent(); + console.log(a); // `1` + + return { b: 2 }; +} +``` + +```js +/// file: src/routes/foo/+page.server.js +// @filename: $types.d.ts +export type PageLoad = import('@sveltejs/kit').Load<{}, null, { a: number, b: number }>; + +// @filename: index.js +// ---cut--- +/** @type {import('./$types').PageLoad} */ +export async function load({ parent }) { + const { a, b } = await parent(); + console.log(a, b); // `1`, `2` + + return { c: 3 }; +} +``` + +In `+page.js` or `+layout.js` it will return data from `load` functions in parent `+layout.js` files. Implicitly, a missing `+layout.js` is treated as a `({ data }) => data` function, meaning that it will also return data from parent `+layout.server.js` files. + +#### setHeaders + +If you need to set headers for the response, you can do so using the `setHeaders` method. This is useful if you want the page to be cached, for example: + +```js +/// file: src/routes/blog/+page.js +/** @type {import('./$types').PageLoad} */ +export async function load({ fetch, setHeaders }) { + const url = `https://cms.example.com/articles.json`; + const response = await fetch(url); + + setHeaders({ + age: response.headers.get('age'), + 'cache-control': response.headers.get('cache-control') + }); + + return response.json(); +} +``` + +> `setHeaders` has no effect when a `load` function runs in the browser. + +Setting the same header multiple times (even in separate `load` functions) is an error — you can only set a given header once. + +The exception is `set-cookie`, which can be set multiple times and can be passed an array of strings: + +```js +/// file: src/routes/+layout.server.js +/** @type {import('./$types').LayoutLoad} */ +export async function load({ setHeaders }) { + setHeaders({ + 'set-cookie': 'a=1; HttpOnly' + }); + + setHeaders({ + 'set-cookie': 'b=2; HttpOnly' + }); + + setHeaders({ + 'set-cookie': ['c=3; HttpOnly', 'd=4; HttpOnly'] + }); +} +``` + +### Output + +Any promises on the returned `data` object will be resolved, if they are top-level properties. This makes it easy to return multiple promises without creating a waterfall: + +```js +// @filename: $types.d.ts +export type PageLoad = import('@sveltejs/kit').Load<{}>; + +// @filename: index.js +// ---cut--- +/** @type {import('./$types').PageLoad} */ +export function load() { + return { + a: Promise.resolve('a'), + b: Promise.resolve('b'), + c: { + value: Promise.resolve('c') + } + }; +} +``` + +```svelte + +``` + +### Errors + +If an error is thrown during `load`, the nearest [`+error.svelte`](/docs/routing#error) will be rendered. For _expected_ 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 +// @filename: ambient.d.ts +declare namespace App { + interface Locals { + user: { + name: string; + isAdmin: boolean; + } + } +} + +// @filename: index.js +// ---cut--- +import { error } from '@sveltejs/kit'; + +/** @type {import('./$types').LayoutServerLoad} */ +export function load({ locals }) { + if (!locals.user) { + throw error(401, 'not logged in'); + } + + if (!locals.user.isAdmin) { + throw error(403, 'not an admin'); + } +} +``` + +If an _unexpected_ error is thrown, SvelteKit will invoke [`handleError`](/docs/hooks#handleerror) and treat it as a 500 Internal Server Error. + +> In development, stack traces for unexpected errors are visible as `$page.error.stack`. In production, stack traces are hidden. + +### Redirects + +To redirect users, use the `redirect` helper from `@sveltejs/kit` to specify the location to which they should be redirected alongside a `3xx` status code. + +```diff +/// file: src/routes/admin/+layout.server.js +-import { error } from '@sveltejs/kit'; ++import { error, redirect } from '@sveltejs/kit'; + +/** @type {import('./$types').LayoutLoad} */ +export function load({ locals }) { + if (!locals.user) { +- throw error(401, 'not logged in'); ++ throw redirect(307, '/login'); + } + + if (!locals.user.isAdmin) { + throw error(403, 'not an admin'); + } +} +``` + +### Invalidation + +SvelteKit tracks the dependencies of each `load` function to avoid re-running it unnecessarily during navigation. For example, a `load` function in a root `+layout.js` doesn't need to re-run when you navigate from one page to another unless it references `url` or a member of `params` that changed since the last navigation. + +Using [`invalidate(url)`](/docs/modules#$app-navigation-invalidate), you can re-run any `load` functions that depend on the invalidated resource (either implicitly, via [`fetch`](#fetch)), or explicitly via [`depends`](#depends). You can also invalidate _all_ `load` functions by calling `invalidate()` without an argument. + +### Shared state + +In many server environments, a single instance of your app will serve multiple users. For that reason, per-request state must not be stored in shared variables outside your `load` functions, but should instead be stored in `event.locals`. Similarly, per-user state must not be stored in global variables, but should instead make use of `$page.data` (which contains the combined data of all `load` functions) or use Svelte's [context feature](https://svelte.dev/docs#run-time-svelte-setcontext) to create scoped state. diff --git a/documentation/docs/05-loading.md b/documentation/docs/05-loading.md deleted file mode 100644 index 332497e6e51d..000000000000 --- a/documentation/docs/05-loading.md +++ /dev/null @@ -1,157 +0,0 @@ ---- -title: Loading ---- - -A component that defines a page or a layout can export a `load` function that runs before the component is created. This function runs both during server-side rendering and in the client, and allows you to fetch and manipulate data before the page is rendered, thus preventing loading spinners. - -If the data for a page comes from its endpoint, you may not need a `load` function. It's useful when you need more flexibility, for example loading data from an external API, which might look like this: - -```html -/// file: src/routes/blog/[slug].svelte - -``` - -> Note the ` +```js +/// file: +page.js/+page.server.js +export const router = false; ``` Note that this will disable client-side routing for any navigation from this page, regardless of whether the router is already active. @@ -26,10 +23,9 @@ Note that this will disable client-side routing for any navigation from this pag Ordinarily, SvelteKit [hydrates](/docs/appendix#hydration) your server-rendered HTML into an interactive page. Some pages don't require JavaScript at all — many blog posts and 'about' pages fall into this category. In these cases you can skip hydration when the app boots up with the app-wide [`browser.hydrate` config option](/docs/configuration#browser) or the page-level `hydrate` export: -```html - +```js +/// file: +page.js/+page.server.js +export const hydrate = false; ``` > If `hydrate` and `router` are both `false`, SvelteKit will not add any JavaScript to the page at all. If [server-side rendering](/docs/hooks#handle) is disabled in `handle`, `hydrate` must be `true` or no content will be rendered. @@ -40,18 +36,16 @@ It's likely that at least some pages of your app can be represented as a simple Prerendering happens automatically for any page with the `prerender` annotation: -```html - +```js +/// file: +page.js/+page.server.js +export const prerender = true; ``` Alternatively, you can set [`config.kit.prerender.default`](/docs/configuration#prerender) to `true` and prerender everything except pages that are explicitly marked as _not_ prerenderable: -```html - +```js +/// file: +page.js/+page.server.js +export const prerender = false; ``` > If your entire app is suitable for prerendering, you can use [`adapter-static`](https://github.com/sveltejs/kit/tree/master/packages/adapter-static), which will output files suitable for use with any static webserver. @@ -64,14 +58,14 @@ The basic rule is this: for a page to be prerenderable, any two users hitting it > Not all pages are suitable for prerendering. Any content that is prerendered will be seen by all users. You can of course fetch personalized data in `onMount` in a prerendered page, but this may result in a poorer user experience since it will involve blank initial content or loading indicators. -Note that you can still prerender pages that load data based on the page's parameters, like our `src/routes/blog/[slug].svelte` example from earlier. The prerenderer will intercept requests made inside `load`, so the data served from `src/routes/blog/[slug].json.js` will also be captured. +Note that you can still prerender pages that load data based on the page's parameters, such as a `src/routes/blog/[slug]/+page.svelte` route. -Accessing [`url.searchParams`](/docs/loading#input-url) during prerendering is forbidden. If you need to use it, ensure you are only doing so in the browser (for example in `onMount`). +Accessing [`url.searchParams`](/docs/load#input-url) during prerendering is forbidden. If you need to use it, ensure you are only doing so in the browser (for example in `onMount`). #### Route conflicts -Because prerendering writes to the filesystem, it isn't possible to have two endpoints that would cause a directory and a file to have the same name. For example, `src/routes/foo/index.js` and `src/routes/foo/bar.js` would try to create `foo` and `foo/bar`, which is impossible. +Because prerendering writes to the filesystem, it isn't possible to have two endpoints that would cause a directory and a file to have the same name. For example, `src/routes/foo/+server.js` and `src/routes/foo/bar/+server.js` would try to create `foo` and `foo/bar`, which is impossible. -For that reason among others, it's recommended that you always include a file extension — `src/routes/foo/index.json.js` and `src/routes/foo/bar.json.js` would result in `foo.json` and `foo/bar.json` files living harmoniously side-by-side. +For that reason among others, it's recommended that you always include a file extension — `src/routes/foo.json/+server.js` and `src/routes/foo/bar.json/+server.js` would result in `foo.json` and `foo/bar.json` files living harmoniously side-by-side. For _pages_, we skirt around this problem by writing `foo/index.html` instead of `foo`. diff --git a/documentation/docs/15-configuration.md b/documentation/docs/15-configuration.md index 80c03e39f25d..60192675068c 100644 --- a/documentation/docs/15-configuration.md +++ b/documentation/docs/15-configuration.md @@ -68,7 +68,6 @@ const config = { onError: 'fail', origin: 'http://sveltekit-prerender' }, - routes: (filepath) => !/(?:(?:^_|\/_)|(?:^\.|\/\.)(?!well-known))/.test(filepath), serviceWorker: { register: true, files: (filepath) => !/\.DS_Store/.test(filepath) @@ -286,10 +285,6 @@ See [Prerendering](/docs/page-options#prerender). An object containing zero or m - `origin` — the value of `url.origin` during prerendering; useful if it is included in rendered content -### routes - -A `(filepath: string) => boolean` function that determines which files create routes and which are treated as [private modules](/docs/routing#private-modules). - ### serviceWorker An object containing zero or more of the following values: diff --git a/documentation/docs/16-types.md b/documentation/docs/16-types.md index 160a6cb42822..2118ab259dc4 100644 --- a/documentation/docs/16-types.md +++ b/documentation/docs/16-types.md @@ -9,7 +9,7 @@ title: Types The `RequestHandler` and `Load` types both accept a `Params` argument allowing you to type the `params` object. For example this endpoint expects `foo`, `bar` and `baz` params: ```js -/// file: src/routes/[foo]/[bar]/[baz].js +/// file: src/routes/[foo]/[bar]/[baz]/+page.server.js // @errors: 2355 /** @type {import('@sveltejs/kit').RequestHandler<{ * foo: string; @@ -26,49 +26,64 @@ Needless to say, this is cumbersome to write out, and less portable (if you were To solve this problem, SvelteKit generates `.d.ts` files for each of your endpoints and pages: ```ts -/// file: .svelte-kit/types/src/routes/[foo]/[bar]/__types/[baz].d.ts +/// file: .svelte-kit/types/src/routes/[foo]/[bar]/[baz]/$types.d.ts /// link: false -import type { RequestHandler as GenericRequestHandler, Load as GenericLoad } from '@sveltejs/kit'; +import type * as Kit from '@sveltejs/kit'; -export type RequestHandler = GenericRequestHandler< - { foo: string; bar: string; baz: string }, - Body ->; +interface RouteParams { + foo: string; + bar: string; + baz: string; +} -export type Load< - InputProps extends Record = Record, - OutputProps extends Record = InputProps -> = GenericLoad<{ foo: string; bar: string; baz: string }, InputProps, OutputProps>; +export type PageServerLoad = Kit.ServerLoad; +export type PageLoad = Kit.Load; ``` These files can be imported into your endpoints and pages as siblings, thanks to the [`rootDirs`](https://www.typescriptlang.org/tsconfig#rootDirs) option in your TypeScript configuration: ```js -/// file: src/routes/[foo]/[bar]/[baz].js -// @filename: __types/[baz].d.ts -import type { RequestHandler as GenericRequestHandler, Load as GenericLoad } from '@sveltejs/kit'; +/// file: src/routes/[foo]/[bar]/[baz]/+page.server.js +// @filename: $types.d.ts +import type * as Kit from '@sveltejs/kit'; + +interface RouteParams { + foo: string; + bar: string; + baz: string; +} -export type RequestHandler = GenericRequestHandler< - { foo: string, bar: string, baz: string }, - Body ->; +export type PageServerLoad = Kit.ServerLoad; // @filename: index.js // @errors: 2355 // ---cut--- -/** @type {import('./__types/[baz]').RequestHandler} */ +/** @type {import('./$types').PageServerLoad} */ export async function GET({ params }) { // ... } ``` -```svelte - +```js +/// file: src/routes/[foo]/[bar]/[baz]/+page.js +// @filename: $types.d.ts +import type * as Kit from '@sveltejs/kit'; + +interface RouteParams { + foo: string; + bar: string; + baz: string; +} + +export type PageLoad = Kit.Load; + +// @filename: index.js +// @errors: 2355 +// ---cut--- +/** @type {import('./$types').PageLoad} */ +export async function load({ params, fetch, session }) { + // ... +} ``` > 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`](/docs/configuration#outdir)): @@ -119,7 +134,7 @@ Others are required for SvelteKit to work properly, and should also be left unto // This ensures both `vite build` // and `svelte-kit package` work correctly - "lib": ["esnext", "DOM"], + "lib": ["esnext", "DOM", "DOM.Iterable"], "moduleResolution": "node", "module": "esnext", "target": "esnext" diff --git a/documentation/docs/17-seo.md b/documentation/docs/17-seo.md index 5e807a09c425..afd79d00fd5a 100644 --- a/documentation/docs/17-seo.md +++ b/documentation/docs/17-seo.md @@ -26,7 +26,7 @@ SvelteKit redirects pathnames with trailing slashes to ones without (or vice ver Every page should have well-written and unique `` and `<meta name="description">` elements inside a [`<svelte:head>`](https://svelte.dev/docs#template-syntax-svelte-head). Guidance on how to write descriptive titles and descriptions, along with other suggestions on making content understandable by search engines, can be found on Google's [Lighthouse SEO audits](https://web.dev/lighthouse-seo/) documentation. -> A common pattern is to return SEO-related [`stuff`](/docs/loading#output-stuff) from page `load` functions, then use it (as [`$page.stuff`](/docs/modules#$app-stores)) in a `<svelte:head>` in your root [layout](/docs/layouts). +> A common pattern is to return SEO-related [`stuff`](/docs/load#output-stuff) from page `load` functions, then use it (as [`$page.stuff`](/docs/modules#$app-stores)) in a `<svelte:head>` in your root [layout](/docs/routing#layout). #### Structured data @@ -57,26 +57,26 @@ export default config; [Sitemaps](https://developers.google.com/search/docs/advanced/sitemaps/build-sitemap) help search engines prioritize pages within your site, particularly when you have a large amount of content. You can create a sitemap dynamically using an endpoint: ```js -/// file: src/routes/sitemap.xml.js +/// file: src/routes/sitemap.xml/+server.js export async function GET() { - return { - headers: { - 'Content-Type': 'application/xml' - }, - body: ` - <?xml version="1.0" encoding="UTF-8" ?> - <urlset - xmlns="https://www.sitemaps.org/schemas/sitemap/0.9" - xmlns:xhtml="https://www.w3.org/1999/xhtml" - xmlns:mobile="https://www.google.com/schemas/sitemap-mobile/1.0" - xmlns:news="https://www.google.com/schemas/sitemap-news/0.9" - xmlns:image="https://www.google.com/schemas/sitemap-image/1.1" - xmlns:video="https://www.google.com/schemas/sitemap-video/1.1" - > - <!-- <url> elements go here --> - </urlset> - `.trim() - }; + return new Response(` + <?xml version="1.0" encoding="UTF-8" ?> + <urlset + xmlns="https://www.sitemaps.org/schemas/sitemap/0.9" + xmlns:xhtml="https://www.w3.org/1999/xhtml" + xmlns:mobile="https://www.google.com/schemas/sitemap-mobile/1.0" + xmlns:news="https://www.google.com/schemas/sitemap-news/0.9" + xmlns:image="https://www.google.com/schemas/sitemap-image/1.1" + xmlns:video="https://www.google.com/schemas/sitemap-video/1.1" + > + <!-- <url> elements go here --> + </urlset>`.trim(), + { + headers: { + 'Content-Type': 'application/xml' + } + } + ); } ``` diff --git a/documentation/docs/80-migrating.md b/documentation/docs/80-migrating.md index 99cc03e49dd4..adad9765a174 100644 --- a/documentation/docs/80-migrating.md +++ b/documentation/docs/80-migrating.md @@ -74,7 +74,14 @@ A common pattern in Sapper apps is to put your internal library in a directory i #### Renamed files -Your custom error page component should be renamed from `_error.svelte` to `__error.svelte`. Any `_layout.svelte` files should likewise be renamed `__layout.svelte`. The double underscore prefix is reserved for SvelteKit; your own [private modules](/docs/routing#private-modules) are still denoted with a single `_` prefix (configurable via [`routes`](/docs/configuration#routes) config). +Routes now are made up of the folder name exclusively to remove ambiguity, the folder names leading up to a `+page.svelte` correspond to the route. See [the routing docs](/docs/routing) for an overview. The following shows a old/new comparison: + +| Old | New | +| ------------------------- | ------------------------- | +| routes/about/index.svelte | routes/about/+page.svelte | +| routes/about.svelte | routes/about/+page.svelte | + +Your custom error page component should be renamed from `_error.svelte` to `+error.svelte`. Any `_layout.svelte` files should likewise be renamed `+layout.svelte`. The double underscore prefix is reserved for SvelteKit; your own [private modules](/docs/routing#private-modules) are still denoted with a single `_` prefix (configurable via [`routes`](/docs/configuration#routes) config). #### Imports @@ -88,7 +95,7 @@ Any files you previously imported from directories in `src/node_modules` will ne As before, pages and layouts can export a function that allows data to be loaded before rendering takes place. -This function has been renamed from `preload` to [`load`](/docs/loading), and its API has changed. Instead of two arguments — `page` and `session` — there is a single argument that includes both, along with `fetch` (which replaces `this.fetch`) and a new `stuff` object. +This function has been renamed from `preload` to [`load`](/docs/load), it now lives in a `+page.js` (or `+layout.js`) next to its `+page.svelte` (or `+layout.svelte`), and its API has changed. Instead of two arguments — `page` and `session` — there is a single argument that includes both, along with `fetch` (which replaces `this.fetch`) and a new `stuff` object. There is no more `this` object, and consequently no `this.fetch`, `this.error` or `this.redirect`. Instead of returning props directly, `load` now returns an object that _contains_ `props`, alongside various other things. diff --git a/packages/adapter-static/test/apps/prerendered/src/routes/index.svelte b/packages/adapter-static/test/apps/prerendered/src/routes/+page.svelte similarity index 100% rename from packages/adapter-static/test/apps/prerendered/src/routes/index.svelte rename to packages/adapter-static/test/apps/prerendered/src/routes/+page.svelte diff --git a/packages/adapter-static/test/apps/prerendered/vite.config.js b/packages/adapter-static/test/apps/prerendered/vite.config.js index c1b5219e47cb..29ad08debe6a 100644 --- a/packages/adapter-static/test/apps/prerendered/vite.config.js +++ b/packages/adapter-static/test/apps/prerendered/vite.config.js @@ -1,11 +1,11 @@ -import { plugin } from '../../utils.js'; +import { sveltekit } from '@sveltejs/kit/vite'; /** @type {import('vite').UserConfig} */ const config = { build: { minify: false }, - plugins: [plugin()] + plugins: [sveltekit()] }; export default config; diff --git a/packages/adapter-static/test/apps/spa/src/routes/__error.svelte b/packages/adapter-static/test/apps/spa/src/routes/+error.svelte similarity index 50% rename from packages/adapter-static/test/apps/spa/src/routes/__error.svelte rename to packages/adapter-static/test/apps/spa/src/routes/+error.svelte index 75e733c98d77..915006a71fbd 100644 --- a/packages/adapter-static/test/apps/spa/src/routes/__error.svelte +++ b/packages/adapter-static/test/apps/spa/src/routes/+error.svelte @@ -1,25 +1,13 @@ -<script context="module"> - /** @type {import('@sveltejs/kit').Load} */ - export function load({ session }) { - return { - props: session - }; - } -</script> - <script> import { browser } from '$app/env'; import { page, session } from '$app/stores'; - /** @type {number} */ - export let count; - if (browser) { $session.count += 1; } </script> <h1>{$page.status}</h1> -<h2>count: {count}</h2> +<h2>count: {$session.count}</h2> <button on:click={() => ($session.count += 1)}>+1</button> diff --git a/packages/adapter-static/test/apps/spa/src/routes/__layout.svelte b/packages/adapter-static/test/apps/spa/src/routes/+layout.svelte similarity index 100% rename from packages/adapter-static/test/apps/spa/src/routes/__layout.svelte rename to packages/adapter-static/test/apps/spa/src/routes/+layout.svelte diff --git a/packages/adapter-static/test/apps/spa/src/routes/index.svelte b/packages/adapter-static/test/apps/spa/src/routes/+page.svelte similarity index 100% rename from packages/adapter-static/test/apps/spa/src/routes/index.svelte rename to packages/adapter-static/test/apps/spa/src/routes/+page.svelte diff --git a/packages/adapter-static/test/apps/spa/src/routes/about.svelte b/packages/adapter-static/test/apps/spa/src/routes/about.svelte deleted file mode 100644 index c898cf32ec92..000000000000 --- a/packages/adapter-static/test/apps/spa/src/routes/about.svelte +++ /dev/null @@ -1,5 +0,0 @@ -<script context="module"> - export const prerender = true; -</script> - -<h1>This page was prerendered</h1> \ No newline at end of file diff --git a/packages/adapter-static/test/apps/spa/src/routes/about/+page.js b/packages/adapter-static/test/apps/spa/src/routes/about/+page.js new file mode 100644 index 000000000000..189f71e2e1b3 --- /dev/null +++ b/packages/adapter-static/test/apps/spa/src/routes/about/+page.js @@ -0,0 +1 @@ +export const prerender = true; diff --git a/packages/adapter-static/test/apps/spa/src/routes/about/+page.svelte b/packages/adapter-static/test/apps/spa/src/routes/about/+page.svelte new file mode 100644 index 000000000000..404fd77511d2 --- /dev/null +++ b/packages/adapter-static/test/apps/spa/src/routes/about/+page.svelte @@ -0,0 +1 @@ +<h1>This page was prerendered</h1> \ No newline at end of file diff --git a/packages/adapter-static/test/apps/spa/vite.config.js b/packages/adapter-static/test/apps/spa/vite.config.js index c1b5219e47cb..29ad08debe6a 100644 --- a/packages/adapter-static/test/apps/spa/vite.config.js +++ b/packages/adapter-static/test/apps/spa/vite.config.js @@ -1,11 +1,11 @@ -import { plugin } from '../../utils.js'; +import { sveltekit } from '@sveltejs/kit/vite'; /** @type {import('vite').UserConfig} */ const config = { build: { minify: false }, - plugins: [plugin()] + plugins: [sveltekit()] }; export default config; diff --git a/packages/adapter-static/test/utils.js b/packages/adapter-static/test/utils.js index 5eec374f3243..be3c90f034ec 100644 --- a/packages/adapter-static/test/utils.js +++ b/packages/adapter-static/test/utils.js @@ -107,7 +107,3 @@ function create_server(port, handler) { function rimraf(path) { (fs.rmSync || fs.rmdirSync)(path, { recursive: true, force: true }); } - -export const plugin = process.env.CI - ? (await import('../../kit/dist/vite.js')).sveltekit - : (await import('../../kit/src/vite/index.js')).sveltekit; diff --git a/packages/create-svelte/templates/default/src/app.d.ts b/packages/create-svelte/templates/default/src/app.d.ts index 76372f545e22..f7e052adaabc 100644 --- a/packages/create-svelte/templates/default/src/app.d.ts +++ b/packages/create-svelte/templates/default/src/app.d.ts @@ -13,6 +13,4 @@ declare namespace App { // interface PublicEnv {} // interface Session {} - - // interface Stuff {} } diff --git a/packages/create-svelte/templates/default/src/routes/__layout.svelte b/packages/create-svelte/templates/default/src/routes/+layout.svelte similarity index 100% rename from packages/create-svelte/templates/default/src/routes/__layout.svelte rename to packages/create-svelte/templates/default/src/routes/+layout.svelte diff --git a/packages/create-svelte/templates/default/src/routes/index.svelte b/packages/create-svelte/templates/default/src/routes/+page.svelte similarity index 91% rename from packages/create-svelte/templates/default/src/routes/index.svelte rename to packages/create-svelte/templates/default/src/routes/+page.svelte index aea2bbdec4ce..15ce68fc5387 100644 --- a/packages/create-svelte/templates/default/src/routes/index.svelte +++ b/packages/create-svelte/templates/default/src/routes/+page.svelte @@ -1,7 +1,3 @@ -<script context="module" lang="ts"> - export const prerender = true; -</script> - <script lang="ts"> import Counter from '$lib/Counter.svelte'; </script> diff --git a/packages/create-svelte/templates/default/src/routes/+page.ts b/packages/create-svelte/templates/default/src/routes/+page.ts new file mode 100644 index 000000000000..189f71e2e1b3 --- /dev/null +++ b/packages/create-svelte/templates/default/src/routes/+page.ts @@ -0,0 +1 @@ +export const prerender = true; diff --git a/packages/create-svelte/templates/default/src/routes/about.svelte b/packages/create-svelte/templates/default/src/routes/about/+page.svelte similarity index 63% rename from packages/create-svelte/templates/default/src/routes/about.svelte rename to packages/create-svelte/templates/default/src/routes/about/+page.svelte index 1817ae41ba3b..b034478a5b15 100644 --- a/packages/create-svelte/templates/default/src/routes/about.svelte +++ b/packages/create-svelte/templates/default/src/routes/about/+page.svelte @@ -1,19 +1,3 @@ -<script context="module"> - import { browser, dev } from '$app/env'; - - // we don't need any JS on this page, though we'll load - // it in dev so that we get hot module replacement... - export const hydrate = dev; - - // ...but if the client-side router is already loaded - // (i.e. we came here from elsewhere in the app), use it - export const router = browser; - - // since there's no dynamic data here, we can prerender - // it so that it gets served as a static asset in prod - export const prerender = true; -</script> - <svelte:head> <title>About diff --git a/packages/create-svelte/templates/default/src/routes/about/+page.ts b/packages/create-svelte/templates/default/src/routes/about/+page.ts new file mode 100644 index 000000000000..3e094ec8e74a --- /dev/null +++ b/packages/create-svelte/templates/default/src/routes/about/+page.ts @@ -0,0 +1,13 @@ +import { browser, dev } from '$app/env'; + +// we don't need any JS on this page, though we'll load +// it in dev so that we get hot module replacement... +export const hydrate = dev; + +// ...but if the client-side router is already loaded +// (i.e. we came here from elsewhere in the app), use it +export const router = browser; + +// since there's no dynamic data here, we can prerender +// it so that it gets served as a static asset in prod +export const prerender = true; diff --git a/packages/create-svelte/templates/default/src/routes/todos/+page.server.ts b/packages/create-svelte/templates/default/src/routes/todos/+page.server.ts new file mode 100644 index 000000000000..e0ba045a51a1 --- /dev/null +++ b/packages/create-svelte/templates/default/src/routes/todos/+page.server.ts @@ -0,0 +1,71 @@ +import { error } from '@sveltejs/kit'; +import { api } from './api'; +import type { PageServerLoad, Action } from './$types'; + +type Todo = { + uid: string; + created_at: Date; + text: string; + done: boolean; + pending_delete: boolean; +}; + +/** + * @typedef {{ + * uid: string; + * created_at: Date; + * text: string; + * done: boolean; + * pending_delete: boolean; + * }} Todo + */ + +/** @type {import('./$types').PageServerLoad} */ +export const load: PageServerLoad = async ({ locals }) => { + // locals.userid comes from src/hooks.js + const response = await api('GET', `todos/${locals.userid}`); + + if (response.status === 404) { + // user hasn't created a todo list. + // start with an empty array + return { + /** @type {Todo[]} */ + todos: [] as Todo[] + }; + } + + if (response.status === 200) { + return { + /** @type {Todo[]} */ + todos: (await response.json()) as Todo[] + }; + } + + throw error(response.status); +}; + +/** @type {import('./$types').Action} */ +export const POST: Action = async ({ request, locals }) => { + const form = await request.formData(); + + await api('POST', `todos/${locals.userid}`, { + text: form.get('text') + }); +}; + +/** @type {import('./$types').Action} */ +export const PATCH: Action = async ({ request, locals }) => { + const form = await request.formData(); + + await api('PATCH', `todos/${locals.userid}/${form.get('uid')}`, { + text: form.has('text') ? form.get('text') : undefined, + done: form.has('done') ? !!form.get('done') : undefined + }); +}; + +/** @type {import('./$types').Action} */ +export const DELETE: Action = async ({ request, locals }) => { + const form = await request.formData(); + + await api('DELETE', `todos/${locals.userid}/${form.get('uid')}`); +}; diff --git a/packages/create-svelte/templates/default/src/routes/todos/index.svelte b/packages/create-svelte/templates/default/src/routes/todos/+page.svelte similarity index 93% rename from packages/create-svelte/templates/default/src/routes/todos/index.svelte rename to packages/create-svelte/templates/default/src/routes/todos/+page.svelte index 4bbe290286fa..2b5a24c54c47 100644 --- a/packages/create-svelte/templates/default/src/routes/todos/index.svelte +++ b/packages/create-svelte/templates/default/src/routes/todos/+page.svelte @@ -2,27 +2,10 @@ import { enhance } from '$lib/form'; import { scale } from 'svelte/transition'; import { flip } from 'svelte/animate'; + import type { PageData } from './$types'; - /** - * @typedef {{ - * uid: string; - * created_at: Date; - * text: string; - * done: boolean; - * pending_delete: boolean; - * }} Todo - */ - - type Todo = { - uid: string; - created_at: Date; - text: string; - done: boolean; - pending_delete: boolean; - }; - - /** @type {Todo[]} */ - export let todos: Todo[]; + /** @type {import('./$types').PageData} */ + export let data: PageData; @@ -46,7 +29,7 @@ - {#each todos as todo (todo.uid)} + {#each data.todos as todo (todo.uid)}
{ - // locals.userid comes from src/hooks.js - const response = await api('GET', `todos/${locals.userid}`); - - if (response.status === 404) { - // user hasn't created a todo list. - // start with an empty array - return { - body: { - todos: [] - } - }; - } - - if (response.status === 200) { - return { - body: { - todos: await response.json() - } - }; - } - - return { - status: response.status - }; -}; - -/** @type {import('./__types').RequestHandler} */ -export const POST: RequestHandler = async ({ request, locals }) => { - const form = await request.formData(); - - await api('POST', `todos/${locals.userid}`, { - text: form.get('text') - }); - - return {}; -}; - -// If the user has JavaScript disabled, the URL will change to -// include the method override unless we redirect back to /todos -const redirect = { - status: 303, - headers: { - location: '/todos' - } -}; - -/** @type {import('./__types').RequestHandler} */ -export const PATCH: RequestHandler = async ({ request, locals }) => { - const form = await request.formData(); - - await api('PATCH', `todos/${locals.userid}/${form.get('uid')}`, { - text: form.has('text') ? form.get('text') : undefined, - done: form.has('done') ? !!form.get('done') : undefined - }); - - return redirect; -}; - -/** @type {import('./__types').RequestHandler} */ -export const DELETE: RequestHandler = async ({ request, locals }) => { - const form = await request.formData(); - - await api('DELETE', `todos/${locals.userid}/${form.get('uid')}`); - - return redirect; -}; diff --git a/packages/create-svelte/templates/skeleton/package.json b/packages/create-svelte/templates/skeleton/package.json new file mode 100644 index 000000000000..a291b3c89754 --- /dev/null +++ b/packages/create-svelte/templates/skeleton/package.json @@ -0,0 +1,7 @@ +{ + "name": "skeleton-template", + "devDependencies": { + "@sveltejs/adapter-auto": "workspace:1.0.0-next.64" + }, + "type": "module" +} diff --git a/packages/create-svelte/templates/skeleton/src/app.d.ts b/packages/create-svelte/templates/skeleton/src/app.d.ts index f69cbcbf5910..f2845e42d8fb 100644 --- a/packages/create-svelte/templates/skeleton/src/app.d.ts +++ b/packages/create-svelte/templates/skeleton/src/app.d.ts @@ -7,5 +7,4 @@ declare namespace App { // interface PrivateEnv {} // interface PublicEnv {} // interface Session {} - // interface Stuff {} } diff --git a/packages/create-svelte/templates/skeleton/src/routes/index.svelte b/packages/create-svelte/templates/skeleton/src/routes/+page.svelte similarity index 100% rename from packages/create-svelte/templates/skeleton/src/routes/index.svelte rename to packages/create-svelte/templates/skeleton/src/routes/+page.svelte diff --git a/packages/kit/package.json b/packages/kit/package.json index 7237f68ab287..cd094c032c4a 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -12,12 +12,20 @@ "dependencies": { "@sveltejs/vite-plugin-svelte": "^1.0.1", "chokidar": "^3.5.3", + "cookie": "^0.5.0", + "devalue": "^2.0.1", + "kleur": "^4.1.4", + "magic-string": "^0.26.2", + "mime": "^3.0.0", + "node-fetch": "^3.2.4", "sade": "^1.8.1", - "tiny-glob": "^0.2.9" + "set-cookie-parser": "^2.4.8", + "sirv": "^2.0.2", + "tiny-glob": "^0.2.9", + "undici": "^5.8.1" }, "devDependencies": { "@playwright/test": "^1.23.4", - "@rollup/plugin-replace": "^4.0.0", "@types/connect": "^3.4.35", "@types/cookie": "^0.5.1", "@types/marked": "^4.0.3", @@ -25,25 +33,12 @@ "@types/node": "^16.11.36", "@types/sade": "^1.7.4", "@types/set-cookie-parser": "^2.4.2", - "cookie": "^0.5.0", - "cross-env": "^7.0.3", - "devalue": "^2.0.1", - "kleur": "^4.1.4", - "locate-character": "^2.0.5", "marked": "^4.0.16", - "mime": "^3.0.0", - "node-fetch": "^3.2.4", "rollup": "^2.75.7", - "selfsigned": "^2.0.1", - "set-cookie-parser": "^2.4.8", - "sirv": "^2.0.2", "svelte": "^3.48.0", - "svelte-check": "^2.7.1", "svelte-preprocess": "^4.10.6", "svelte2tsx": "~0.5.10", - "tiny-glob": "^0.2.9", "typescript": "^4.7.4", - "undici": "^5.8.1", "uvu": "^0.5.3", "vite": "^3.0.0" }, @@ -55,23 +50,25 @@ "svelte-kit": "svelte-kit.js" }, "files": [ - "assets", - "dist", + "src", + "!src/**/*.spec.js", + "!src/packaging/test", + "!src/core/**/fixtures", + "!src/core/sync/create_manifest_data/test", "types", "svelte-kit.js" ], "scripts": { - "build": "rollup -c && node scripts/cp.js src/runtime/components assets/components && npm run types", + "build": "npm run types", "dev": "rollup -cw", "lint": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore", "check": "tsc", "check:all": "tsc && pnpm -r --filter=\"./**\" check", "format": "npm run lint -- --write", "prepublishOnly": "npm run build", - "test": "npm run test:unit && npm run test:typings && npm run test:packaging && npm run test:integration", + "test": "npm run test:unit && npm run test:packaging && npm run test:integration", "test:integration": "pnpm run -r --workspace-concurrency 1 --filter=\"./test/**\" test", "test:unit": "uvu src \"(spec\\.js|test[\\\\/]index\\.js)\" -i packaging", - "test:typings": "tsc --project test/typings", "test:packaging": "uvu src/packaging \"(spec\\.js|test[\\\\/]index\\.js)\"", "types": "node scripts/extract-types.js", "postinstall": "node svelte-kit.js sync" @@ -79,19 +76,20 @@ "exports": { "./package.json": "./package.json", ".": { + "import": "./src/index/index.js", "types": "./types/index.d.ts" }, "./node": { - "import": "./dist/node.js" + "import": "./src/node/index.js" }, "./node/polyfills": { - "import": "./dist/node/polyfills.js" + "import": "./src/node/polyfills.js" }, "./hooks": { - "import": "./dist/hooks.js" + "import": "./src/hooks.js" }, "./vite": { - "import": "./dist/vite.js" + "import": "./src/vite/index.js" } }, "types": "types/index.d.ts", diff --git a/packages/kit/rollup.config.js b/packages/kit/rollup.config.js deleted file mode 100644 index 884eba331230..000000000000 --- a/packages/kit/rollup.config.js +++ /dev/null @@ -1,97 +0,0 @@ -import commonjs from '@rollup/plugin-commonjs'; -import fs from 'fs'; -import replace from '@rollup/plugin-replace'; -import resolve from '@rollup/plugin-node-resolve'; -import pkg from './package.json'; - -(fs.rmSync || fs.rmdirSync)('assets', { recursive: true, force: true }); - -const external = [].concat( - Object.keys(pkg.dependencies || {}), - Object.keys(pkg.peerDependencies || {}), - Object.keys(process.binding('natives')), - 'typescript', - 'svelte2tsx' -); - -export default [ - { - input: { - 'client/start': 'src/runtime/client/start.js', - 'client/singletons': 'src/runtime/client/singletons.js', - 'app/navigation': 'src/runtime/app/navigation.js', - 'app/stores': 'src/runtime/app/stores.js', - 'app/paths': 'src/runtime/app/paths.js', - 'app/env': 'src/runtime/app/env.js', - 'env/dynamic/private': 'src/runtime/env/dynamic/private.js', - 'env/dynamic/public': 'src/runtime/env/dynamic/public.js', - 'env-private': 'src/runtime/env-private.js', - 'env-public': 'src/runtime/env-public.js', - paths: 'src/runtime/paths.js', - env: 'src/runtime/env.js' - }, - output: { - dir: 'assets', - format: 'esm', - chunkFileNames: 'chunks/[name].js' - }, - external: [ - 'svelte', - 'svelte/store', - '__GENERATED__/root.svelte', - '__GENERATED__/client-manifest.js' - ], - plugins: [ - resolve({ - extensions: ['.mjs', '.js', '.ts'] - }) - ] - }, - - { - input: 'src/runtime/server/index.js', - output: { - format: 'esm', - file: 'assets/server/index.js' - }, - plugins: [ - resolve({ - extensions: ['.mjs', '.js', '.ts'] - }), - commonjs() - ] - }, - - { - input: { - cli: 'src/cli.js', - hooks: 'src/hooks.js', - node: 'src/node/index.js', - 'node/polyfills': 'src/node/polyfills.js', - prerender: 'src/core/prerender/prerender.js', - vite: 'src/vite/index.js' - }, - output: { - dir: 'dist', - format: 'esm', - chunkFileNames: 'chunks/[name].js' - }, - external: (id) => { - return id.startsWith('node:') || external.includes(id); - }, - plugins: [ - replace({ - preventAssignment: true, - values: { - __VERSION__: pkg.version, - 'process.env.BUNDLED': 'true' - } - }), - resolve({ - extensions: ['.mjs', '.js', '.ts'] - }), - commonjs() - ], - preserveEntrySignatures: true - } -]; diff --git a/packages/kit/scripts/extract-types.js b/packages/kit/scripts/extract-types.js index b3b8b17f2909..f0611052097e 100644 --- a/packages/kit/scripts/extract-types.js +++ b/packages/kit/scripts/extract-types.js @@ -92,7 +92,7 @@ function strip_origin(str) { modules.push({ name: '@sveltejs/kit', - comment: 'The following types can be imported from `@sveltejs/kit`:', + comment: 'The following can be imported from `@sveltejs/kit`:', ...get_types(code, node.statements) }); } diff --git a/packages/kit/src/cli.js b/packages/kit/src/cli.js index f896733c0a95..54b5ecd17b74 100755 --- a/packages/kit/src/cli.js +++ b/packages/kit/src/cli.js @@ -19,7 +19,8 @@ function handle_error(e) { process.exit(1); } -const prog = sade('svelte-kit').version('__VERSION__'); +const pkg = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8')); +const prog = sade('svelte-kit').version(pkg.version); prog .command('package') @@ -67,7 +68,7 @@ prog try { const config = await load_config(); const sync = await import('./core/sync/sync.js'); - sync.all(config, mode); + await sync.all(config, mode); } catch (error) { handle_error(error); } diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index c3755870bbe1..f5d3a6451020 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -23,8 +23,9 @@ export function create_builder({ config, build_data, prerendered, log }) { /** @param {import('types').RouteData} route */ // TODO routes should come pre-filtered function not_prerendered(route) { - if (route.type === 'page' && route.path) { - return !prerendered_paths.has(route.path) && !prerendered_paths.has(route.path + '/'); + const path = route.type === 'page' && !route.id.includes('[') && `/${route.id}`; + if (path) { + return !prerendered_paths.has(path) && !prerendered_paths.has(path + '/'); } return true; diff --git a/packages/kit/src/core/config/index.spec.js b/packages/kit/src/core/config/index.spec.js index 1467c74bb779..4d31adcf9a18 100644 --- a/packages/kit/src/core/config/index.spec.js +++ b/packages/kit/src/core/config/index.spec.js @@ -120,6 +120,7 @@ const get_defaults = (prefix = '') => ({ }, protocol: undefined, router: undefined, + routes: undefined, ssr: undefined, target: undefined, trailingSlash: 'never', diff --git a/packages/kit/src/core/config/options.js b/packages/kit/src/core/config/options.js index c83d3e9b009c..7abba322254a 100644 --- a/packages/kit/src/core/config/options.js +++ b/packages/kit/src/core/config/options.js @@ -291,7 +291,11 @@ const options = object( // TODO remove for 1.0 router: error((keypath) => `${keypath} has been moved to config.kit.browser.router`), - routes: fun((filepath) => !/(?:(?:^_|\/_)|(?:^\.|\/\.)(?!well-known))/.test(filepath)), + // TODO remove for 1.0 + routes: error( + (keypath) => + `${keypath} has been removed. See https://github.com/sveltejs/kit/discussions/5774 for details` + ), serviceWorker: object({ register: boolean(true), diff --git a/packages/kit/src/core/generate_manifest/index.js b/packages/kit/src/core/generate_manifest/index.js index 983c8501e99c..946043484bb9 100644 --- a/packages/kit/src/core/generate_manifest/index.js +++ b/packages/kit/src/core/generate_manifest/index.js @@ -14,33 +14,14 @@ import { get_mime_lookup } from '../utils.js'; */ export function generate_manifest({ build_data, relative_path, routes, format = 'esm' }) { /** @typedef {{ index: number, path: string }} LookupEntry */ - /** @type {Map} */ + /** @type {Map} */ const bundled_nodes = new Map(); - // 0 and 1 are special, they correspond to the root layout and root error nodes - bundled_nodes.set(build_data.manifest_data.components[0], { - path: `${relative_path}/nodes/0.js`, - index: 0 - }); - - bundled_nodes.set(build_data.manifest_data.components[1], { - path: `${relative_path}/nodes/1.js`, - index: 1 - }); - - routes.forEach((route) => { - if (route.type === 'page') { - [...route.a, ...route.b].forEach((component) => { - if (component && !bundled_nodes.has(component)) { - const i = build_data.manifest_data.components.indexOf(component); - - bundled_nodes.set(component, { - path: `${relative_path}/nodes/${i}.js`, - index: bundled_nodes.size - }); - } - }); - } + build_data.manifest_data.nodes.forEach((node, i) => { + bundled_nodes.set(node, { + path: `${relative_path}/nodes/${i}.js`, + index: i + }); }); /** @type {(path: string) => string} */ @@ -57,7 +38,7 @@ export function generate_manifest({ build_data, relative_path, routes, format = assets.push(build_data.service_worker); } - /** @param {string | undefined} id */ + /** @param {import('types').PageNode | undefined} id */ const get_index = (id) => id && /** @type {LookupEntry} */ (bundled_nodes.get(id)).index; const matchers = new Set(); @@ -87,10 +68,9 @@ export function generate_manifest({ build_data, relative_path, routes, format = pattern: ${pattern}, names: ${s(names)}, types: ${s(types)}, - path: ${route.path ? s(route.path) : null}, - shadow: ${route.shadow ? loader(`${relative_path}/${build_data.server.vite_manifest[route.shadow].file}`) : null}, - a: ${s(route.a.map(get_index))}, - b: ${s(route.b.map(get_index))} + errors: ${s(route.errors.map(get_index))}, + layouts: ${s(route.layouts.map(get_index))}, + leaf: ${s(get_index(route.leaf))} }`.replace(/^\t\t/gm, ''); } else { if (!build_data.server.vite_manifest[route.file]) { diff --git a/packages/kit/src/core/prerender/prerender.js b/packages/kit/src/core/prerender/prerender.js index 80471728d105..93d535efdd6c 100644 --- a/packages/kit/src/core/prerender/prerender.js +++ b/packages/kit/src/core/prerender/prerender.js @@ -343,7 +343,7 @@ export async function prerender() { /** @type {import('types').ManifestData} */ const { routes } = (await import(pathToFileURL(manifest_path).href)).manifest._; const entries = routes - .map((route) => (route.type === 'page' ? route.path : '')) + .map((route) => (route.type === 'page' && !route.id.includes('[') ? `/${route.id}` : '')) .filter(Boolean); for (const entry of entries) { diff --git a/packages/kit/src/core/sync/copy_assets.js b/packages/kit/src/core/sync/copy_assets.js deleted file mode 100644 index 9f0360a1a822..000000000000 --- a/packages/kit/src/core/sync/copy_assets.js +++ /dev/null @@ -1,25 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { copy } from '../../utils/filesystem.js'; -import { fileURLToPath } from 'url'; - -const filename = fileURLToPath(import.meta.url); -const dirname = path.dirname(filename); - -/** @param {string} dest */ -export function copy_assets(dest) { - if (!process.env.BUNDLED) return; - let prefix = '..'; - do { - // we jump through these hoops so that this function - // works whether or not it's been bundled - const resolved = path.resolve(dirname, `${prefix}/assets`); - - if (fs.existsSync(resolved)) { - copy(resolved, dest); - return; - } - - prefix = `../${prefix}`; - } while (true); -} diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index 004b3a1f39de..797767c73aa6 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -1,51 +1,10 @@ import fs from 'fs'; import path from 'path'; import mime from 'mime'; -import { get_runtime_directory } from '../../utils.js'; +import { runtime_directory } from '../../utils.js'; import { posixify } from '../../../utils/filesystem.js'; import { parse_route_id } from '../../../utils/routing.js'; -/** - * A portion of a file or directory name where the name has been split into - * static and dynamic parts - * @typedef {{ - * content: string; - * dynamic: boolean; - * rest: boolean; - * type: string | null; - * }} Part - */ - -/** - * A route, consisting of an endpoint module and/or an array of components - * (n layouts and one leaf) for successful navigations and an array of - * n error components to render if navigation fails - * @typedef {{ - * id: string; - * pattern: RegExp; - * segments: Part[][]; - * page?: { - * a: Array; - * b: Array; - * }; - * endpoint?: string; - * }} Unit - */ - -/** - * @typedef {{ - * error: string | undefined; - * layouts: Record - * }} Node - */ - -/** - * @typedef {Map} Tree - */ - -const layout_pattern = /^__layout(?:-([a-zA-Z0-9_-]+))?(?:@([a-zA-Z0-9_-]+))?$/; -const dunder_pattern = /(^|\/)__(?!tests?__)/; // forbid __-prefixed files/directories except __error, __layout[-...], __test__, __tests__ - const DEFAULT = 'default'; /** @@ -58,198 +17,218 @@ const DEFAULT = 'default'; */ export default function create_manifest_data({ config, - fallback = `${get_runtime_directory(config.kit)}/components`, + fallback = `${runtime_directory}/components`, cwd = process.cwd() }) { - /** @type {import('types').RouteData[]} */ - const routes = []; + /** @type {Map} */ + const route_map = new Map(); - /** @type {Map} */ - const units = new Map(); + /** @type {Map} */ + const segment_map = new Map(); - /** @type {Tree} */ + /** @type {import('./types').RouteTree} */ const tree = new Map(); const default_layout = { - file: posixify(path.relative(cwd, `${fallback}/layout.svelte`)), - name: DEFAULT + component: posixify(path.relative(cwd, `${fallback}/layout.svelte`)) }; // set default root layout/error tree.set('', { - error: posixify(path.relative(cwd, `${fallback}/error.svelte`)), + error: { + component: posixify(path.relative(cwd, `${fallback}/error.svelte`)) + }, layouts: { [DEFAULT]: default_layout } }); + /** @param {string} id */ + function tree_node(id) { + if (!tree.has(id)) { + tree.set(id, { + error: undefined, + layouts: {} + }); + } + + return /** @type {import('./types').RouteTreeNode} */ (tree.get(id)); + } + const routes_base = posixify(path.relative(cwd, config.kit.files.routes)); const valid_extensions = [...config.extensions, ...config.kit.moduleExtensions]; if (fs.existsSync(config.kit.files.routes)) { - list_files(config.kit.files.routes).forEach((file) => { - const extension = valid_extensions.find((ext) => file.endsWith(ext)); + list_files(config.kit.files.routes).forEach((filepath) => { + const extension = valid_extensions.find((ext) => filepath.endsWith(ext)); if (!extension) return; - const id = file - .slice(0, -extension.length) - .replace(/(?:^|\/)index((?:@[a-zA-Z0-9_-]+)?(?:\.[a-z]+)?)?$/, '$1'); - const project_relative = `${routes_base}/${file}`; + const project_relative = `${routes_base}/${filepath}`; + const segments = filepath.split('/'); + const file = /** @type {string} */ (segments.pop()); + + if (file[0] !== '+') return; // not a route file - const segments = id.split('/'); - const name = /** @type {string} */ (segments.pop()); + const item = analyze(file, config.extensions, config.kit.moduleExtensions); - if (name === '__layout.reset') { + if (!item) { throw new Error( - '__layout.reset has been removed in favour of named layouts: https://kit.svelte.dev/docs/layouts#named-layouts' + `Files and directories prefixed with + are reserved (saw ${project_relative})` ); } - if (name === '__error' || layout_pattern.test(name)) { - const dir = segments.join('/'); + const id = segments.join('/'); - if (!tree.has(dir)) { - tree.set(dir, { - error: undefined, - layouts: {} - }); - } + if (/\]\[/.test(id)) { + throw new Error(`Invalid route ${project_relative} — parameters must be separated`); + } - const group = /** @type {Node} */ (tree.get(dir)); + if (count_occurrences('[', id) !== count_occurrences(']', id)) { + throw new Error(`Invalid route ${project_relative} — brackets are unbalanced`); + } - if (name === '__error') { - group.error = project_relative; - } else { - const match = /** @type {RegExpMatchArray} */ (layout_pattern.exec(name)); + // error/layout files should be added to the tree, but don't result + // in a route being created, so deal with them first. note: we are + // relying on the fact that the +error and +layout files precede + // +page files alphabetically, and will therefore be processes + // before we reach the page + if (item.kind === 'component' && item.is_error) { + tree_node(id).error = { + component: project_relative + }; - if (match[1] === DEFAULT) { - throw new Error(`${project_relative} cannot use reserved "${DEFAULT}" name`); - } + return; + } + + if (item.is_layout) { + if (item.declares_layout === DEFAULT) { + throw new Error(`${project_relative} cannot use reserved "${DEFAULT}" name`); + } - const layout_id = match[1] || DEFAULT; + const layout_id = item.declares_layout || DEFAULT; - const defined = group.layouts[layout_id]; - if (defined && defined !== default_layout) { - throw new Error( - `Duplicate layout ${project_relative} already defined at ${defined.file}` - ); - } + const group = tree_node(id); - group.layouts[layout_id] = { - file: project_relative, - name - }; + const defined = group.layouts[layout_id] || (group.layouts[layout_id] = {}); + + if (defined[item.kind] && layout_id !== DEFAULT) { + // edge case + throw new Error( + `Duplicate layout ${project_relative} already defined at ${defined[item.kind]}` + ); } + defined[item.kind] = project_relative; + return; - } else if (dunder_pattern.test(file)) { - throw new Error( - `Files and directories prefixed with __ are reserved (saw ${project_relative})` - ); } - if (!config.kit.routes(file)) return; + const type = item.kind === 'server' && !item.is_layout && !item.is_page ? 'endpoint' : 'page'; - if (/\]\[/.test(id)) { - throw new Error(`Invalid route ${project_relative} — parameters must be separated`); + if (type === 'endpoint' && route_map.has(id)) { + // note that we are relying on +server being lexically ordered after + // all other route files — if we added +view or something this is + // potentially brittle, since the server might be added before + // another route file. a problem for another day + throw new Error( + `${file} cannot share a directory with other route files (${project_relative})` + ); } - if (count_occurrences('[', id) !== count_occurrences(']', id)) { - throw new Error(`Invalid route ${project_relative} — brackets are unbalanced`); - } + if (!route_map.has(id)) { + const pattern = parse_route_id(id).pattern; - if (!units.has(id)) { - units.set(id, { + segment_map.set( id, - pattern: parse_route_id(id).pattern, - segments: id - .split('/') - .filter(Boolean) - .map((segment) => { - /** @type {Part[]} */ - const parts = []; - segment.split(/\[(.+?)\]/).map((content, i) => { - const dynamic = !!(i % 2); - - if (!content) return; - - parts.push({ - content, - dynamic, - rest: dynamic && content.startsWith('...'), - type: (dynamic && content.split('=')[1]) || null - }); + segments.filter(Boolean).map((segment) => { + /** @type {import('./types').Part[]} */ + const parts = []; + segment.split(/\[(.+?)\]/).map((content, i) => { + const dynamic = !!(i % 2); + + if (!content) return; + + parts.push({ + content, + dynamic, + rest: dynamic && content.startsWith('...'), + type: (dynamic && content.split('=')[1]) || null }); - return parts; - }), - page: undefined, - endpoint: undefined - }); + }); + return parts; + }) + ); + + if (type === 'endpoint') { + route_map.set(id, { + type, + id, + pattern, + file: project_relative + }); + } else { + route_map.set(id, { + type, + id, + pattern, + errors: [], + layouts: [], + leaf: {} + }); + } } - const unit = /** @type {Unit} */ (units.get(id)); + if (item.is_page) { + const route = /** @type {import('types').PageData} */ (route_map.get(id)); - if (config.extensions.find((ext) => file.endsWith(ext))) { - const { layouts, errors } = trace(project_relative, file, tree, config.extensions); - unit.page = { - a: layouts.concat(project_relative), - b: errors - }; - } else { - unit.endpoint = project_relative; + if (item.kind === 'component') { + route.leaf.component = project_relative; + + const { layouts, errors } = trace(tree, id, item.uses_layout, project_relative); + route.layouts = layouts; + route.errors = errors; + } else if (item.kind === 'server') { + route.leaf.server = project_relative; + } else { + route.leaf.shared = project_relative; + } } }); + + // TODO remove for 1.0 + if (route_map.size === 0) { + throw new Error( + 'The filesystem router API has changed, see https://github.com/sveltejs/kit/discussions/5774 for details' + ); + } } - /** @type {string[]} */ - const components = []; + /** @type {import('types').PageNode[]} */ + const nodes = []; tree.forEach(({ layouts, error }) => { // we do [default, error, ...other_layouts] so that components[0] and [1] // are the root layout/error. kinda janky, there's probably a nicer way if (layouts[DEFAULT]) { - components.push(layouts[DEFAULT].file); + nodes.push(layouts[DEFAULT]); } if (error) { - components.push(error); + nodes.push(error); } for (const id in layouts) { - if (id !== DEFAULT) components.push(layouts[id].file); + if (id !== DEFAULT) { + nodes.push(layouts[id]); + } } }); - units.forEach((unit) => { - if (unit.page) { - const leaf = /** @type {string} */ (unit.page.a[unit.page.a.length - 1]); - components.push(leaf); + route_map.forEach((route) => { + if (route.type === 'page') { + nodes.push(route.leaf); } }); - Array.from(units.values()) - .sort(compare) - .forEach((unit) => { - // TODO when we introduce layout endpoints and scoped middlewares, we - // will probably want to have a single unified route type here - // (created in the list_files(...).forEach(...) callback) - if (unit.page) { - routes.push({ - type: 'page', - id: unit.id, - pattern: unit.pattern, - path: unit.id.includes('[') ? '' : `/${unit.id.replace(/@(?:[a-zA-Z0-9_-]+)/g, '')}`, - shadow: unit.endpoint || null, - a: unit.page.a, - b: unit.page.b - }); - } else if (unit.endpoint) { - routes.push({ - type: 'endpoint', - id: unit.id, - pattern: unit.pattern, - file: unit.endpoint - }); - } - }); + const routes = Array.from(route_map.values()).sort((a, b) => compare(a, b, segment_map)); /** @type {import('types').Asset[]} */ const assets = fs.existsSync(config.kit.files.assets) @@ -289,7 +268,7 @@ export default function create_manifest_data({ return { assets, - components, + nodes, routes, matchers }; @@ -297,39 +276,80 @@ export default function create_manifest_data({ /** * @param {string} file - * @param {string} path - * @param {Tree} tree - * @param {string[]} extensions + * @param {string[]} component_extensions + * @param {string[]} module_extensions + * @returns {import('./types').RouteFile | null} */ -function trace(file, path, tree, extensions) { - /** @type {Array} */ - const layouts = []; +function analyze(file, component_extensions, module_extensions) { + const component_extension = component_extensions.find((ext) => file.endsWith(ext)); + if (component_extension) { + const name = file.slice(0, -component_extension.length); + const pattern = + /^\+(?:(page(?:@([a-zA-Z0-9_-]+))?)|(layout(?:-([a-zA-Z0-9_-]+))?(?:@([a-zA-Z0-9_-]+))?)|(error))$/; + const match = pattern.exec(name); + if (!match) return null; + + return { + kind: 'component', + is_page: !!match[1], + is_layout: !!match[3], + is_error: !!match[6], + uses_layout: match[2] || match[5], + declares_layout: match[4] + }; + } - /** @type {Array} */ - const errors = []; + const module_extension = module_extensions.find((ext) => file.endsWith(ext)); + if (module_extension) { + const name = file.slice(0, -module_extension.length); + const pattern = /^\+(?:(server)|(page(\.server)?)|(layout(?:-([a-zA-Z0-9_-]+))?(\.server)?))$/; + const match = pattern.exec(name); + if (!match) return null; + + const kind = !!(match[1] || match[3] || match[6]) ? 'server' : 'shared'; + + return { + kind, + is_page: !!match[2], + is_layout: !!match[4], + declares_layout: match[5] + }; + } - const parts = path.split('/'); - const filename = /** @type {string} */ (parts.pop()); - const extension = /** @type {string} */ (extensions.find((ext) => path.endsWith(ext))); - const base = filename.slice(0, -extension.length); + return null; +} - let layout_id = base.includes('@') ? base.split('@')[1] : DEFAULT; +/** + * @param {import('./types').RouteTree} tree + * @param {string} id + * @param {string} layout_id + * @param {string} project_relative + */ +function trace(tree, id, layout_id = DEFAULT, project_relative) { + /** @type {Array} */ + const layouts = []; - if (parts.findIndex((part) => part.indexOf('@') > -1) > -1) { - throw new Error(`Invalid route ${file} - named layouts are not allowed in directories`); - } + /** @type {Array} */ + const errors = []; + + const parts = id.split('/').filter(Boolean); - // walk up the tree, find which __layout and __error components + // walk up the tree, find which +layout and +error components // apply to this page while (true) { const node = tree.get(parts.join('/')); const layout = node?.layouts[layout_id]; - if (layout?.file && layouts.indexOf(layout.file) > -1) { - throw new Error(`Recursive layout detected: ${layout.file} -> ${layouts.join(' -> ')}`); + if (layout && layouts.indexOf(layout) > -1) { + // TODO this needs to be fixed for #5748 + throw new Error( + `Recursive layout detected: ${layout.component} -> ${layouts + .map((l) => l?.component) + .join(' -> ')}` + ); } - // any segment that has neither a __layout nor an __error can be discarded. + // any segment that has neither a +layout nor an +error can be discarded. // in other words these... // layouts: [a, , b, c] // errors: [d, , e, ] @@ -337,13 +357,15 @@ function trace(file, path, tree, extensions) { // ...can be compacted to these: // layouts: [a, b, c] // errors: [d, e, ] - if (node?.error || layout?.file) { + if (node?.error || layout) { errors.unshift(node?.error); - layouts.unshift(layout?.file); + layouts.unshift(layout); } - if (layout?.name.includes('@')) { - layout_id = layout.name.split('@')[1]; + const parent_layout_id = layout?.component?.split('@')[1]?.split('.')[0]; + + if (parent_layout_id) { + layout_id = parent_layout_id; } else { if (layout) layout_id = DEFAULT; if (parts.length === 0) break; @@ -352,7 +374,7 @@ function trace(file, path, tree, extensions) { } if (layout_id !== DEFAULT) { - throw new Error(`${file} references missing layout "${layout_id}"`); + throw new Error(`${project_relative} references missing layout "${layout_id}"`); } // trim empty space off the end of the errors array @@ -364,14 +386,18 @@ function trace(file, path, tree, extensions) { } /** - * @param {Unit} a - * @param {Unit} b + * @param {import('types').RouteData} a + * @param {import('types').RouteData} b + * @param {Map} segment_map */ -function compare(a, b) { - const max_segments = Math.max(a.segments.length, b.segments.length); +function compare(a, b, segment_map) { + const a_segments = /** @type {import('./types').Part[][]} */ (segment_map.get(a.id)); + const b_segments = /** @type {import('./types').Part[][]} */ (segment_map.get(b.id)); + + const max_segments = Math.max(a_segments.length, b_segments.length); for (let i = 0; i < max_segments; i += 1) { - const sa = a.segments[i]; - const sb = b.segments[i]; + const sa = a_segments[i]; + const sb = b_segments[i]; // /x < /x/y, but /[...x]/y < /[...x] if (!sa) return a.id.includes('[...') ? +1 : -1; @@ -405,8 +431,8 @@ function compare(a, b) { } } - const a_is_endpoint = !a.page && a.endpoint; - const b_is_endpoint = !b.page && b.endpoint; + const a_is_endpoint = a.type === 'endpoint'; + const b_is_endpoint = b.type === 'endpoint'; if (a_is_endpoint !== b_is_endpoint) { return a_is_endpoint ? -1 : +1; @@ -435,12 +461,12 @@ function count_occurrences(needle, haystack) { function list_files(dir, path = '', files = []) { fs.readdirSync(dir) .sort((a, b) => { - // sort each directory in (__layout, __error, everything else) order + // sort each directory in (+layout, +error, everything else) order // so that we can trace layouts/errors immediately - if (a.startsWith('__layout')) { - if (!b.startsWith('__layout')) return -1; - } else if (b.startsWith('__layout')) { + if (a.startsWith('+layout')) { + if (!b.startsWith('+layout')) return -1; + } else if (b.startsWith('+layout')) { return 1; } else if (a.startsWith('__')) { if (!b.startsWith('__')) return -1; diff --git a/packages/kit/src/core/sync/create_manifest_data/index.spec.js b/packages/kit/src/core/sync/create_manifest_data/index.spec.js index 03e9ed21b118..a841e923d4a1 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.spec.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.spec.js @@ -26,72 +26,73 @@ const create = (dir, config = {}) => { }); }; -const default_layout = 'layout.svelte'; -const default_error = 'error.svelte'; +const default_layout = { + component: 'layout.svelte' +}; + +const default_error = { + component: 'error.svelte' +}; test('creates routes', () => { - const { components, routes } = create('samples/basic'); + const { nodes, routes } = create('samples/basic'); - const index = 'samples/basic/index.svelte'; - const about = 'samples/basic/about.svelte'; - const blog = 'samples/basic/blog/index.svelte'; - const blog_$slug = 'samples/basic/blog/[slug].svelte'; + const index = { component: 'samples/basic/+page.svelte' }; + const about = { component: 'samples/basic/about/+page.svelte' }; + const blog = { component: 'samples/basic/blog/+page.svelte' }; + const blog_$slug = { component: 'samples/basic/blog/[slug]/+page.svelte' }; - assert.equal(components, [default_layout, default_error, about, blog_$slug, blog, index]); + assert.equal(nodes, [default_layout, default_error, index, about, blog, blog_$slug]); assert.equal(routes, [ { type: 'page', id: '', pattern: /^\/$/, - path: '/', - shadow: null, - a: [default_layout, index], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + leaf: index }, { type: 'endpoint', id: 'blog.json', pattern: /^\/blog\.json$/, - file: 'samples/basic/blog/index.json.js' + file: 'samples/basic/blog.json/+server.js' }, { type: 'page', id: 'about', pattern: /^\/about\/?$/, - path: '/about', - shadow: null, - a: [default_layout, about], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + leaf: about }, { type: 'page', id: 'blog', pattern: /^\/blog\/?$/, - path: '/blog', - shadow: null, - a: [default_layout, blog], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + leaf: blog }, { type: 'endpoint', id: 'blog/[slug].json', pattern: /^\/blog\/([^/]+?)\.json$/, - file: 'samples/basic/blog/[slug].json.ts' + file: 'samples/basic/blog/[slug].json/+server.ts' }, { type: 'page', id: 'blog/[slug]', pattern: /^\/blog\/([^/]+?)\/?$/, - path: '', - shadow: null, - a: [default_layout, blog_$slug], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + leaf: blog_$slug } ]); }); @@ -103,86 +104,85 @@ const symlink_survived_git = fs const test_symlinks = symlink_survived_git ? test : test.skip; test_symlinks('creates symlinked routes', () => { - const { components, routes } = create('samples/symlinks/routes'); + const { nodes, routes } = create('samples/symlinks/routes'); - const index = 'samples/symlinks/routes/index.svelte'; - const symlinked_index = 'samples/symlinks/routes/foo/index.svelte'; + const index = { component: 'samples/symlinks/routes/index.svelte' }; + const symlinked_index = { component: 'samples/symlinks/routes/foo/index.svelte' }; - assert.equal(components, [default_layout, default_error, symlinked_index, index]); + assert.equal(nodes, [default_layout, default_error, symlinked_index, index]); assert.equal(routes, [ { type: 'page', id: '', pattern: /^\/$/, - path: '/', - shadow: null, - a: [default_layout, index], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + leaf: index }, { type: 'page', id: 'foo', pattern: /^\/foo\/?$/, - path: '/foo', - shadow: null, - a: [default_layout, symlinked_index], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + leaf: symlinked_index } ]); }); test('creates routes with layout', () => { - const { components, routes } = create('samples/basic-layout'); + const { nodes, routes } = create('samples/basic-layout'); - const layout = 'samples/basic-layout/__layout.svelte'; - const index = 'samples/basic-layout/index.svelte'; - const foo___layout = 'samples/basic-layout/foo/__layout.svelte'; - const foo = 'samples/basic-layout/foo/index.svelte'; + const layout = { component: 'samples/basic-layout/+layout.svelte' }; + const index = { component: 'samples/basic-layout/+page.svelte' }; + const foo___layout = { component: 'samples/basic-layout/foo/+layout.svelte' }; + const foo = { component: 'samples/basic-layout/foo/+page.svelte' }; - assert.equal(components, [layout, default_error, foo___layout, foo, index]); + assert.equal(nodes, [layout, default_error, foo___layout, index, foo]); - assert.equal(routes, [ - { - type: 'page', - id: '', - pattern: /^\/$/, - path: '/', - shadow: null, - a: [layout, index], - b: [default_error] - }, + assert.equal( + routes.slice(1, 2), + [ + { + type: 'page', + id: '', + pattern: /^\/$/, + errors: [default_error], + layouts: [layout], + leaf: index + }, - { - type: 'page', - id: 'foo', - pattern: /^\/foo\/?$/, - path: '/foo', - shadow: null, - a: [layout, foo___layout, foo], - b: [default_error] - } - ]); + { + type: 'page', + id: 'foo', + pattern: /^\/foo\/?$/, + errors: [default_error], + layouts: [layout, foo___layout], + leaf: foo + } + ].slice(1, 2) + ); }); test('succeeds when routes does not exist', () => { - const { components, routes } = create('samples/basic/routes'); - assert.equal(components, ['layout.svelte', 'error.svelte']); + const { nodes, routes } = create('samples/basic/routes'); + assert.equal(nodes, [default_layout, default_error]); assert.equal(routes, []); }); // TODO some characters will need to be URL-encoded in the filename test('encodes invalid characters', () => { - const { components, routes } = create('samples/encoding'); + const { nodes, routes } = create('samples/encoding'); // had to remove ? and " because windows // const quote = 'samples/encoding/".svelte'; - const hash = 'samples/encoding/%23.svelte'; + const hash = { component: 'samples/encoding/%23/+page.svelte' }; // const question_mark = 'samples/encoding/?.svelte'; - assert.equal(components, [ + assert.equal(nodes, [ default_layout, default_error, // quote, @@ -231,13 +231,30 @@ test('sorts routes correctly', () => { test('sorts routes with rest correctly', () => { const { routes } = create('samples/rest'); - assert.equal( - routes.map((p) => (p.type === 'page' ? p.a : p.file)), - [ - [default_layout, 'samples/rest/a/[...rest].svelte'], - [default_layout, 'samples/rest/b/[...rest].svelte'] - ] - ); + assert.equal(routes, [ + { + type: 'page', + id: 'a/[...rest]', + pattern: /^\/a(?:\/(.*))?\/?$/, + errors: [default_error], + layouts: [default_layout], + leaf: { + component: 'samples/rest/a/[...rest]/+page.svelte', + server: 'samples/rest/a/[...rest]/+page.server.js' + } + }, + { + type: 'page', + id: 'b/[...rest]', + pattern: /^\/b(?:\/(.*))?\/?$/, + errors: [default_error], + layouts: [default_layout], + leaf: { + component: 'samples/rest/b/[...rest]/+page.svelte', + server: 'samples/rest/b/[...rest]/+page.server.ts' + } + } + ]); }); test('allows rest parameters inside segments', () => { @@ -248,16 +265,17 @@ test('allows rest parameters inside segments', () => { type: 'page', id: 'prefix-[...rest]', pattern: /^\/prefix-(.*?)\/?$/, - path: '', - shadow: null, - a: [default_layout, 'samples/rest-prefix-suffix/prefix-[...rest].svelte'], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + leaf: { + component: 'samples/rest-prefix-suffix/prefix-[...rest]/+page.svelte' + } }, { type: 'endpoint', id: '[...rest].json', pattern: /^\/(.*?)\.json$/, - file: 'samples/rest-prefix-suffix/[...rest].json.js' + file: 'samples/rest-prefix-suffix/[...rest].json/+server.js' } ]); }); @@ -266,7 +284,7 @@ test('ignores files and directories with leading underscores', () => { const { routes } = create('samples/hidden-underscore'); assert.equal(routes.map((r) => r.type === 'endpoint' && r.file).filter(Boolean), [ - 'samples/hidden-underscore/e/f/g/h.js' + 'samples/hidden-underscore/e/f/g/h/+server.js' ]); }); @@ -274,38 +292,10 @@ test('ignores files and directories with leading dots except .well-known', () => const { routes } = create('samples/hidden-dot'); assert.equal(routes.map((r) => r.type === 'endpoint' && r.file).filter(Boolean), [ - 'samples/hidden-dot/.well-known/dnt-policy.txt.js' + 'samples/hidden-dot/.well-known/dnt-policy.txt/+server.js' ]); }); -test('ignores files by `kit.excludes` config w/RegExp', () => { - const { routes } = create('samples/hidden-by-excludes-config', { - kit: { - routes: (filepath) => - !filepath.endsWith('.test.js') && - !filepath.endsWith('.spec.js') && - !filepath.endsWith('.md') - } - }); - - assert.equal( - routes - .map((r) => r.type === 'endpoint' && r.file) - .filter(Boolean) - .sort(), - [ - 'samples/hidden-by-excludes-config/.a.js', - 'samples/hidden-by-excludes-config/.well-known/dnt-policy.txt.js', - 'samples/hidden-by-excludes-config/_a.js', - 'samples/hidden-by-excludes-config/a.js', - 'samples/hidden-by-excludes-config/subdir/.a.js', - 'samples/hidden-by-excludes-config/subdir/_a.js', - 'samples/hidden-by-excludes-config/subdir/.well-known/dnt-policy.txt.js', - 'samples/hidden-by-excludes-config/subdir/a.js' - ].sort() - ); -}); - test('allows multiple slugs', () => { const { routes } = create('samples/multiple-slugs'); @@ -316,7 +306,7 @@ test('allows multiple slugs', () => { type: 'endpoint', id: '[file].[ext]', pattern: /^\/([^/]+?)\.([^/]+?)$/, - file: 'samples/multiple-slugs/[file].[ext].js' + file: 'samples/multiple-slugs/[file].[ext]/+server.js' } ] ); @@ -325,7 +315,7 @@ test('allows multiple slugs', () => { test('fails if dynamic params are not separated', () => { assert.throws(() => { create('samples/invalid-params'); - }, /Invalid route samples\/invalid-params\/\[foo\]\[bar\]\.js — parameters must be separated/); + }, /Invalid route samples\/invalid-params\/\[foo\]\[bar\]\/\+server\.js — parameters must be separated/); }); test('ignores things that look like lockfiles', () => { @@ -335,77 +325,73 @@ test('ignores things that look like lockfiles', () => { { type: 'endpoint', id: 'foo', - file: 'samples/lockfiles/foo.js', + file: 'samples/lockfiles/foo/+server.js', pattern: /^\/foo\/?$/ } ]); }); test('works with custom extensions', () => { - const { components, routes } = create('samples/custom-extension', { + const { nodes, routes } = create('samples/custom-extension', { extensions: ['.jazz', '.beebop', '.funk', '.svelte'] }); - const index = 'samples/custom-extension/index.funk'; - const about = 'samples/custom-extension/about.jazz'; - const blog = 'samples/custom-extension/blog/index.svelte'; - const blog_$slug = 'samples/custom-extension/blog/[slug].beebop'; + const index = { component: 'samples/custom-extension/+page.funk' }; + const about = { component: 'samples/custom-extension/about/+page.jazz' }; + const blog = { component: 'samples/custom-extension/blog/+page.svelte' }; + const blog_$slug = { component: 'samples/custom-extension/blog/[slug]/+page.beebop' }; - assert.equal(components, [default_layout, default_error, about, blog_$slug, blog, index]); + assert.equal(nodes, [default_layout, default_error, index, about, blog, blog_$slug]); assert.equal(routes, [ { type: 'page', id: '', pattern: /^\/$/, - path: '/', - shadow: null, - a: [default_layout, index], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + leaf: index }, { type: 'endpoint', id: 'blog.json', pattern: /^\/blog\.json$/, - file: 'samples/custom-extension/blog/index.json.js' + file: 'samples/custom-extension/blog.json/+server.js' }, { type: 'page', id: 'about', pattern: /^\/about\/?$/, - path: '/about', - shadow: null, - a: [default_layout, about], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + leaf: about }, { type: 'page', id: 'blog', pattern: /^\/blog\/?$/, - path: '/blog', - shadow: null, - a: [default_layout, blog], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + leaf: blog }, { type: 'endpoint', id: 'blog/[slug].json', pattern: /^\/blog\/([^/]+?)\.json$/, - file: 'samples/custom-extension/blog/[slug].json.js' + file: 'samples/custom-extension/blog/[slug].json/+server.js' }, { type: 'page', id: 'blog/[slug]', pattern: /^\/blog\/([^/]+?)\/?$/, - path: '', - shadow: null, - a: [default_layout, blog_$slug], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + leaf: blog_$slug } ]); }); @@ -435,52 +421,49 @@ test('includes nested error components', () => { type: 'page', id: 'foo/bar/baz', pattern: /^\/foo\/bar\/baz\/?$/, - path: '/foo/bar/baz', - shadow: null, - a: [ - default_layout, - 'samples/nested-errors/foo/__layout.svelte', + errors: [ + default_error, undefined, - 'samples/nested-errors/foo/bar/baz/__layout.svelte', - 'samples/nested-errors/foo/bar/baz/index.svelte' + { component: 'samples/nested-errors/foo/bar/+error.svelte' }, + { component: 'samples/nested-errors/foo/bar/baz/+error.svelte' } ], - b: [ - default_error, + layouts: [ + default_layout, + { component: 'samples/nested-errors/foo/+layout.svelte' }, undefined, - 'samples/nested-errors/foo/bar/__error.svelte', - 'samples/nested-errors/foo/bar/baz/__error.svelte' - ] + { component: 'samples/nested-errors/foo/bar/baz/+layout.svelte' } + ], + leaf: { + component: 'samples/nested-errors/foo/bar/baz/+page.svelte' + } } ]); }); -test('errors on encountering an illegal __file', () => { - assert.throws( - () => create('samples/illegal-dunder'), - /Files and directories prefixed with __ are reserved \(saw samples\/illegal-dunder\/__foo.svelte\)/ - ); -}); - test('creates routes with named layouts', () => { - const { components, routes } = create('samples/named-layouts'); + const { nodes, routes } = create('samples/named-layouts'); - assert.equal(components, [ - 'samples/named-layouts/__layout.svelte', + assert.equal(nodes, [ + { component: 'samples/named-layouts/+layout.svelte' }, default_error, - 'samples/named-layouts/__layout-home@default.svelte', - 'samples/named-layouts/__layout-special.svelte', - 'samples/named-layouts/a/__layout.svelte', - 'samples/named-layouts/b/__layout-alsospecial@special.svelte', - 'samples/named-layouts/b/c/__layout.svelte', - 'samples/named-layouts/b/d/__layout-extraspecial@special.svelte', - 'samples/named-layouts/b/d/__layout-special.svelte', - 'samples/named-layouts/a/a1.svelte', - 'samples/named-layouts/a/a2@special.svelte', - 'samples/named-layouts/b/c/c1@alsospecial.svelte', - 'samples/named-layouts/b/c/c2@home.svelte', - 'samples/named-layouts/b/d/d1.svelte', - 'samples/named-layouts/b/d/d2@extraspecial.svelte', - 'samples/named-layouts/b/d/index@special.svelte' + { component: 'samples/named-layouts/+layout-home@default.svelte' }, + { + component: 'samples/named-layouts/+layout-special.svelte', + shared: 'samples/named-layouts/+layout-special.js', + server: 'samples/named-layouts/+layout-special.server.js' + }, + { component: 'samples/named-layouts/a/+layout.svelte' }, + { component: 'samples/named-layouts/b/+layout-alsospecial@special.svelte' }, + { component: 'samples/named-layouts/b/c/+layout.svelte' }, + { component: 'samples/named-layouts/b/d/+layout-extraspecial@special.svelte' }, + { component: 'samples/named-layouts/b/d/+layout-special.svelte' }, + { component: 'samples/named-layouts/a/a1/+page.svelte' }, + { component: 'samples/named-layouts/a/a2/+page@special.svelte' }, + { component: 'samples/named-layouts/b/c/c1/+page@alsospecial.svelte' }, + { component: 'samples/named-layouts/b/c/c2/+page@home.svelte' }, + { component: 'samples/named-layouts/b/d/+page@special.svelte' }, + { component: 'samples/named-layouts/b/d/d1/+page.svelte' }, + { component: 'samples/named-layouts/b/d/d2/+page@extraspecial.svelte' } ]); assert.equal(routes, [ @@ -488,88 +471,83 @@ test('creates routes with named layouts', () => { type: 'page', id: 'a/a1', pattern: /^\/a\/a1\/?$/, - path: '/a/a1', - shadow: null, - a: [ - 'samples/named-layouts/__layout.svelte', - 'samples/named-layouts/a/__layout.svelte', - 'samples/named-layouts/a/a1.svelte' + errors: [default_error], + layouts: [ + { component: 'samples/named-layouts/+layout.svelte' }, + { component: 'samples/named-layouts/a/+layout.svelte' } ], - b: [default_error] + leaf: { component: 'samples/named-layouts/a/a1/+page.svelte' } }, { type: 'page', - id: 'a/a2@special', + id: 'a/a2', pattern: /^\/a\/a2\/?$/, - path: '/a/a2', - shadow: null, - a: [ - 'samples/named-layouts/__layout-special.svelte', - 'samples/named-layouts/a/a2@special.svelte' + errors: [default_error], + layouts: [ + { + component: 'samples/named-layouts/+layout-special.svelte', + shared: 'samples/named-layouts/+layout-special.js', + server: 'samples/named-layouts/+layout-special.server.js' + } ], - b: [default_error] + leaf: { component: 'samples/named-layouts/a/a2/+page@special.svelte' } }, { type: 'page', - id: 'b/d@special', + id: 'b/d', pattern: /^\/b\/d\/?$/, - path: '/b/d', - shadow: null, - a: [ - 'samples/named-layouts/__layout.svelte', - 'samples/named-layouts/b/d/__layout-special.svelte', - 'samples/named-layouts/b/d/index@special.svelte' + errors: [default_error], + layouts: [ + { component: 'samples/named-layouts/+layout.svelte' }, + { component: 'samples/named-layouts/b/d/+layout-special.svelte' } ], - b: [default_error] + leaf: { component: 'samples/named-layouts/b/d/+page@special.svelte' } }, { type: 'page', - id: 'b/c/c1@alsospecial', + id: 'b/c/c1', pattern: /^\/b\/c\/c1\/?$/, - path: '/b/c/c1', - shadow: null, - a: [ - 'samples/named-layouts/__layout-special.svelte', - 'samples/named-layouts/b/__layout-alsospecial@special.svelte', - 'samples/named-layouts/b/c/c1@alsospecial.svelte' + errors: [default_error], + layouts: [ + { + component: 'samples/named-layouts/+layout-special.svelte', + shared: 'samples/named-layouts/+layout-special.js', + server: 'samples/named-layouts/+layout-special.server.js' + }, + { component: 'samples/named-layouts/b/+layout-alsospecial@special.svelte' } ], - b: [default_error] + leaf: { component: 'samples/named-layouts/b/c/c1/+page@alsospecial.svelte' } }, { type: 'page', - id: 'b/c/c2@home', + id: 'b/c/c2', pattern: /^\/b\/c\/c2\/?$/, - path: '/b/c/c2', - shadow: null, - a: [ - 'samples/named-layouts/__layout.svelte', - 'samples/named-layouts/__layout-home@default.svelte', - 'samples/named-layouts/b/c/c2@home.svelte' + errors: [default_error, default_error], + layouts: [ + { component: 'samples/named-layouts/+layout.svelte' }, + { component: 'samples/named-layouts/+layout-home@default.svelte' } ], - b: [default_error, default_error] + leaf: { component: 'samples/named-layouts/b/c/c2/+page@home.svelte' } }, { type: 'page', id: 'b/d/d1', pattern: /^\/b\/d\/d1\/?$/, - path: '/b/d/d1', - shadow: null, - a: ['samples/named-layouts/__layout.svelte', 'samples/named-layouts/b/d/d1.svelte'], - b: [default_error] + errors: [default_error], + layouts: [{ component: 'samples/named-layouts/+layout.svelte' }], + leaf: { component: 'samples/named-layouts/b/d/d1/+page.svelte' } }, { type: 'page', - id: 'b/d/d2@extraspecial', + id: 'b/d/d2', pattern: /^\/b\/d\/d2\/?$/, - path: '/b/d/d2', - shadow: null, - a: [ - 'samples/named-layouts/__layout.svelte', - 'samples/named-layouts/b/d/__layout-special.svelte', - 'samples/named-layouts/b/d/__layout-extraspecial@special.svelte', - 'samples/named-layouts/b/d/d2@extraspecial.svelte' + errors: [default_error], + layouts: [ + { component: 'samples/named-layouts/+layout.svelte' }, + { component: 'samples/named-layouts/b/d/+layout-special.svelte' }, + { component: 'samples/named-layouts/b/d/+layout-extraspecial@special.svelte' } ], - b: [default_error] + leaf: { component: 'samples/named-layouts/b/d/d2/+page@extraspecial.svelte' } } ]); }); @@ -577,58 +555,40 @@ test('creates routes with named layouts', () => { test('errors on missing layout', () => { assert.throws( () => create('samples/named-layout-missing'), - /samples\/named-layout-missing\/index@missing.svelte references missing layout "missing"/ + /samples\/named-layout-missing\/\+page@missing.svelte references missing layout "missing"/ ); }); test('errors on layout named default', () => { assert.throws( () => create('samples/named-layout-default'), - /samples\/named-layout-default\/__layout-default.svelte cannot use reserved "default" name/ + /samples\/named-layout-default\/\+layout-default.svelte cannot use reserved "default" name/ ); }); test('errors on duplicate layout definition', () => { assert.throws( () => create('samples/duplicate-layout'), - /Duplicate layout samples\/duplicate-layout\/__layout-a@x.svelte already defined at samples\/duplicate-layout\/__layout-a.svelte/ + /Duplicate layout samples\/duplicate-layout\/\+layout-a@x.svelte already defined at samples\/duplicate-layout\/\+layout-a.svelte/ ); }); test('errors on recursive name layout', () => { assert.throws( () => create('samples/named-layout-recursive-1'), - /Recursive layout detected: samples\/named-layout-recursive-1\/__layout-a@b\.svelte -> samples\/named-layout-recursive-1\/__layout-b@a\.svelte -> samples\/named-layout-recursive-1\/__layout-a@b\.svelte/ + /Recursive layout detected: samples\/named-layout-recursive-1\/\+layout-a@b\.svelte -> samples\/named-layout-recursive-1\/\+layout-b@a\.svelte -> samples\/named-layout-recursive-1\/\+layout-a@b\.svelte/ ); assert.throws( () => create('samples/named-layout-recursive-2'), - /Recursive layout detected: samples\/named-layout-recursive-2\/__layout-a@a\.svelte -> samples\/named-layout-recursive-2\/__layout-a@a\.svelte/ + /Recursive layout detected: samples\/named-layout-recursive-2\/\+layout-a@a\.svelte -> samples\/named-layout-recursive-2\/\+layout-a@a\.svelte/ ); assert.throws( () => create('samples/named-layout-recursive-3'), - /Recursive layout detected: samples\/named-layout-recursive-3\/__layout@a\.svelte -> samples\/named-layout-recursive-3\/__layout-a@default\.svelte -> samples\/named-layout-recursive-3\/__layout@a\.svelte/ - ); -}); - -test('errors on layout in directory', () => { - assert.throws( - () => create('samples/named-layout-on-directory-1'), - /Invalid route samples\/named-layout-on-directory-1\/foo@a\/index.svelte - named layouts are not allowed in directories/ - ); - - assert.throws( - () => create('samples/named-layout-on-directory-2'), - /Invalid route samples\/named-layout-on-directory-2\/foo@a\/bar.svelte - named layouts are not allowed in directories/ + /Recursive layout detected: samples\/named-layout-recursive-3\/\+layout@a\.svelte -> samples\/named-layout-recursive-3\/\+layout-a@default\.svelte -> samples\/named-layout-recursive-3\/\+layout@a\.svelte/ ); }); -test('allows for __tests__ directories', () => { - const { routes } = create('samples/legal-dunder'); - - assert.equal(routes, []); -}); - test('creates param matchers', () => { const { matchers } = create('samples/basic'); // directory doesn't matter for the test @@ -654,7 +614,9 @@ test('errors on duplicate matchers', () => { try { assert.throws(() => { create('samples/basic', { - extensions: ['.js', '.ts'] + kit: { + moduleExtensions: ['.js', '.ts'] + } }); }, /Duplicate matchers/); } finally { diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/+layout.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/+layout.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/about.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic/about.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug].json.ts b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/about/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug].json.ts rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic/about/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug].svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog.json/+server.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug].svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog.json/+server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/_default.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/_default.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/index.json.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug].json/+server.ts similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/index.json.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug].json/+server.ts diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/default.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/default.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/about.jazz b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/+page.funk similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/about.jazz rename to packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/+page.funk diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug].beebop b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/about/+page.jazz similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug].beebop rename to packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/about/+page.jazz diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug].json.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog.json/+server.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug].json.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog.json/+server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/index.json.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/index.json.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug].json/+server.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug].json/+server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/index.funk b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug]/+page.beebop similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/index.funk rename to packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug]/+page.beebop diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/__layout-a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/+layout-a.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/__layout-a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/+layout-a.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/__layout-a@x.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/+layout-a@x.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/__layout-a@x.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/+layout-a@x.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/encoding/%23.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/encoding/%23/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/encoding/%23.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/encoding/%23/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/.a.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-dot/.well-known/dnt-policy.txt/+server.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/.a.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-dot/.well-known/dnt-policy.txt/+server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/.well-known/dnt-policy.txt.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/.well-known/dnt-policy.txt.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/__error.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/e/f/g/h/+server.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/__error.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/e/f/g/h/+server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-params/[foo][bar]/+server.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-params/[foo][bar]/+server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/_a.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo/+server.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/_a.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo/+server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo/+server.js_tmp similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo/+server.js_tmp diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.md b/packages/kit/src/core/sync/create_manifest_data/test/samples/multiple-slugs/[file].[ext]/+server.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.md rename to packages/kit/src/core/sync/create_manifest_data/test/samples/multiple-slugs/[file].[ext]/+server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.spec.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-default/+layout-default.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.spec.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-default/+layout-default.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/.a.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-missing/+page@missing.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/.a.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-missing/+page@missing.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/.well-known/dnt-policy.txt.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/+layout-a@b.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/.well-known/dnt-policy.txt.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/+layout-a@b.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/__error.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/+layout-b@a.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/__error.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/+layout-b@a.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/+page@a.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/+page@a.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/_a.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/+layout-a@a.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/_a.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/+layout-a@a.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/+page@a.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/+page@a.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.md b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/+layout-a@default.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.md rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/+layout-a@default.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.spec.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/+layout@a.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.spec.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/+layout@a.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-dot/.well-known/dnt-policy.txt.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-dot/.well-known/dnt-policy.txt.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/e/f/g/h.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout-home@default.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/e/f/g/h.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout-home@default.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout-special.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout-special.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/illegal-dunder/__foo.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout-special.server.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/illegal-dunder/__foo.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout-special.server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-params/[foo][bar].js b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout-special.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-params/[foo][bar].js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout-special.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-qualifier/[foo([a-z]([0-9]))].js b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/+layout.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-qualifier/[foo([a-z]([0-9]))].js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/legal-dunder/__test__/legal.test.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a1/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/legal-dunder/__test__/legal.test.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a1/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/legal-dunder/__tests__/legal.test.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a2/+page@special.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/legal-dunder/__tests__/legal.test.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a2/+page@special.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/+layout-alsospecial@special.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/+layout-alsospecial@special.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo.js_tmp b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/+layout.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo.js_tmp rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/multiple-slugs/[file].[ext].js b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c1/+page@alsospecial.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/multiple-slugs/[file].[ext].js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c1/+page@alsospecial.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-default/__layout-default.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c2/+page@home.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-default/__layout-default.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c2/+page@home.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-missing/index@missing.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/+layout-extraspecial@special.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-missing/index@missing.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/+layout-extraspecial@special.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/__layout-a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/+layout-special.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/__layout-a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/+layout-special.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/foo@a/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/+page@special.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/foo@a/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/+page@special.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/__layout-a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d1/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/__layout-a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d1/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/foo@a/bar.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d2/+page@extraspecial.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/foo@a/bar.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d2/+page@extraspecial.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/__layout-a@b.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/+layout.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/__layout-a@b.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/__layout-b@a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/+error.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/__layout-b@a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/+error.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/index@a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/+error.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/index@a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/+error.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/__layout-a@a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/+layout.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/__layout-a@a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/index@a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/index@a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/__layout-a@default.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/[...rest].json/+server.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/__layout-a@default.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/[...rest].json/+server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/__layout@a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/prefix-[...rest]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/__layout@a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/prefix-[...rest]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest]/+page.server.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest]/+page.server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/__layout-home@default.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/__layout-home@default.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/__layout-special.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest]/+page.server.ts similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/__layout-special.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest]/+page.server.ts diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a1.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a1.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a2@special.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...anotherrest]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a2@special.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...anotherrest]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/__layout-alsospecial@special.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/__layout-alsospecial@special.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/abc/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/abc/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c1@alsospecial.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c1@alsospecial.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c2@home.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c2@home.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/__layout-extraspecial@special.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/xyz/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/__layout-extraspecial@special.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/xyz/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/__layout-special.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[endpoint]/+server.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/__layout-special.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[endpoint]/+server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d1.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[wildcard]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d1.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[wildcard]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d2@extraspecial.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/about/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d2@extraspecial.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/about/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/index@special.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/index@special.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/[id]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/[id]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/__error.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/bar/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/__error.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/bar/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/__error.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[xx]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/__error.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[xx]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].json/+server.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].json/+server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[zz].ts b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[zz].ts deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/[...rest].json.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[zz]/+server.ts similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/[...rest].json.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[zz]/+server.ts diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/foo.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/foo.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/prefix-[...rest].svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/foo/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/prefix-[...rest].svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/foo/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/index.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest].js b/packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/bar/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest].js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/bar/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/bar/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/bar/index.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest].svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/routes/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest].svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/routes/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/routes/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/routes/index.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/types.d.ts b/packages/kit/src/core/sync/create_manifest_data/types.d.ts new file mode 100644 index 000000000000..ac63955a9001 --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/types.d.ts @@ -0,0 +1,40 @@ +import { PageNode } from 'types'; + +interface Part { + content: string; + dynamic: boolean; + rest: boolean; + type: string | null; +} + +interface RouteTreeNode { + error: PageNode | undefined; + layouts: Record; +} + +export type RouteTree = Map; + +interface RouteComponent { + kind: 'component'; + is_page: boolean; + is_layout: boolean; + is_error: boolean; + uses_layout: string | undefined; + declares_layout: string | undefined; +} + +interface RouteSharedModule { + kind: 'shared'; + is_page: boolean; + is_layout: boolean; + declares_layout: string | undefined; +} + +interface RouteServerModule { + kind: 'server'; + is_page: boolean; + is_layout: boolean; + declares_layout: string | undefined; +} + +export type RouteFile = RouteComponent | RouteSharedModule | RouteServerModule; diff --git a/packages/kit/src/core/sync/sync.js b/packages/kit/src/core/sync/sync.js index a7517f5f5a86..60e71ba0f767 100644 --- a/packages/kit/src/core/sync/sync.js +++ b/packages/kit/src/core/sync/sync.js @@ -1,11 +1,10 @@ import path from 'path'; import create_manifest_data from './create_manifest_data/index.js'; -import { copy_assets } from './copy_assets.js'; -import { write_manifest } from './write_manifest.js'; +import { write_client_manifest } from './write_client_manifest.js'; import { write_matchers } from './write_matchers.js'; import { write_root } from './write_root.js'; import { write_tsconfig } from './write_tsconfig.js'; -import { write_types } from './write_types.js'; +import { write_type, write_types } from './write_types.js'; import { write_ambient } from './write_ambient.js'; /** @@ -14,26 +13,37 @@ import { write_ambient } from './write_ambient.js'; * @param {string} mode */ export function init(config, mode) { - copy_assets(path.join(config.kit.outDir, 'runtime')); - write_tsconfig(config.kit); write_ambient(config.kit, mode); } /** - * Update SvelteKit's generated files. + * Update SvelteKit's generated files * @param {import('types').ValidatedConfig} config */ -export function update(config) { +export async function create(config) { const manifest_data = create_manifest_data({ config }); const output = path.join(config.kit.outDir, 'generated'); - const base = path.relative('.', output); - write_manifest(manifest_data, base, output); + write_client_manifest(manifest_data, output); write_root(manifest_data, output); write_matchers(manifest_data, output); - write_types(config, manifest_data); + await write_types(config, manifest_data); + + return { manifest_data }; +} + +/** + * Update SvelteKit's generated files in response to a single file content update. + * Do not call this when the file in question was created/deleted. + * + * @param {import('types').ValidatedConfig} config + * @param {import('types').ManifestData} manifest_data + * @param {string} file + */ +export async function update(config, manifest_data, file) { + await write_type(config, manifest_data, file); return { manifest_data }; } @@ -43,7 +53,7 @@ export function update(config) { * @param {import('types').ValidatedConfig} config * @param {string} mode The Vite mode */ -export function all(config, mode) { +export async function all(config, mode) { init(config, mode); - return update(config); + return await create(config); } diff --git a/packages/kit/src/core/sync/utils.js b/packages/kit/src/core/sync/utils.js index d8cce64dec8c..dafb6350f193 100644 --- a/packages/kit/src/core/sync/utils.js +++ b/packages/kit/src/core/sync/utils.js @@ -25,6 +25,17 @@ export function write(file, code) { fs.writeFileSync(file, code); } +/** + * @param {(file: string) => boolean} should_remove + */ +export function remove_from_previous(should_remove) { + for (const key of previous_contents.keys()) { + if (should_remove(key)) { + previous_contents.delete(key); + } + } +} + /** @param {string} str */ export function trim(str) { const indentation = /** @type {RegExpExecArray} */ (/\n?(\s*)/.exec(str))[1]; diff --git a/packages/kit/src/core/sync/write_client_manifest.js b/packages/kit/src/core/sync/write_client_manifest.js new file mode 100644 index 000000000000..9178c9d95bda --- /dev/null +++ b/packages/kit/src/core/sync/write_client_manifest.js @@ -0,0 +1,82 @@ +import { relative } from 'path'; +import { s } from '../../utils/misc.js'; +import { trim, write_if_changed } from './utils.js'; + +/** + * Writes the client manifest to disk. The manifest is used to power the router. It contains the + * list of routes and corresponding Svelte components (i.e. pages and layouts). + * @param {import('types').ManifestData} manifest_data + * @param {string} output + */ +export function write_client_manifest(manifest_data, output) { + /** @type {Map} */ + const node_indexes = new Map(); + + /** + * Creates a module that exports a `CSRPageNode` + * @param {import('types').PageNode} node + */ + function generate_node(node) { + const declarations = []; + + if (node.shared) { + declarations.push( + `import * as shared from ${s(relative(`${output}/nodes`, node.shared))};`, + `export { shared };` + ); + } + + if (node.component) { + declarations.push( + `export { default as component } from ${s(relative(`${output}/nodes`, node.component))};` + ); + } + + if (node.server) { + declarations.push(`export const server = true;`); + } + + return declarations.join('\n'); + } + + const nodes = manifest_data.nodes + .map((node, i) => { + node_indexes.set(node, i); + write_if_changed(`${output}/nodes/${i}.js`, generate_node(node)); + return `() => import('./nodes/${i}')`; + }) + .join(',\n\t'); + + const dictionary = `{ + ${manifest_data.routes + .map((route) => { + if (route.type === 'page') { + const errors = route.errors.map((node) => (node ? node_indexes.get(node) : '')).join(','); + const layouts = route.layouts + .map((node) => (node ? node_indexes.get(node) : '')) + .join(','); + const leaf = route.leaf ? node_indexes.get(route.leaf) : ''; + + const uses_server_data = [...route.layouts, route.leaf].some((node) => node?.server); + const suffix = uses_server_data ? ', 1' : ''; + + return `${s(route.id)}: [[${errors}], [${layouts}], ${leaf}${suffix}]`; + } + }) + .filter(Boolean) + .join(',\n\t\t')} + }`.replace(/^\t/gm, ''); + + write_if_changed( + `${output}/client-manifest.js`, + trim(` + export { matchers } from './client-matchers.js'; + + export const nodes = [ + ${nodes} + ]; + + export const dictionary = ${dictionary}; + `) + ); +} diff --git a/packages/kit/src/core/sync/write_manifest.js b/packages/kit/src/core/sync/write_manifest.js deleted file mode 100644 index f76eac2bbaff..000000000000 --- a/packages/kit/src/core/sync/write_manifest.js +++ /dev/null @@ -1,57 +0,0 @@ -import path from 'path'; -import { s } from '../../utils/misc.js'; -import { trim, write_if_changed } from './utils.js'; - -/** - * Writes the client manifest to disk. The manifest is used to power the router. It contains the - * list of routes and corresponding Svelte components (i.e. pages and layouts). - * @param {import('types').ManifestData} manifest_data - * @param {string} base - * @param {string} output - */ -export function write_manifest(manifest_data, base, output) { - /** @type {Record} */ - const component_indexes = {}; - - /** @param {string} c */ - const get_path = (c) => path.relative(base, c); - - const components = `[ - ${manifest_data.components - .map((component, i) => { - component_indexes[component] = i; - - return `() => import(${s(get_path(component))})`; - }) - .join(',\n\t\t\t\t\t')} - ]`.replace(/^\t/gm, ''); - - /** @param {Array} parts */ - const get_indices = (parts) => - `[${parts.map((part) => (part ? component_indexes[part] : '')).join(', ')}]`; - - const dictionary = `{ - ${manifest_data.routes - .map((route) => { - if (route.type === 'page') { - const tuple = [get_indices(route.a), get_indices(route.b)]; - if (route.shadow) tuple.push('1'); - - return `${s(route.id)}: [${tuple.join(', ')}]`; - } - }) - .filter(Boolean) - .join(',\n\t\t')} - }`.replace(/^\t/gm, ''); - - write_if_changed( - `${output}/client-manifest.js`, - trim(` - export { matchers } from './client-matchers.js'; - - export const components = ${components}; - - export const dictionary = ${dictionary}; - `) - ); -} diff --git a/packages/kit/src/core/sync/write_root.js b/packages/kit/src/core/sync/write_root.js index 9aa745236ffb..c71e30f5b9d4 100644 --- a/packages/kit/src/core/sync/write_root.js +++ b/packages/kit/src/core/sync/write_root.js @@ -9,7 +9,7 @@ export function write_root(manifest_data, output) { const max_depth = Math.max( ...manifest_data.routes.map((route) => - route.type === 'page' ? route.a.filter(Boolean).length : 0 + route.type === 'page' ? route.layouts.filter(Boolean).length + 1 : 0 ), 1 ); @@ -21,16 +21,16 @@ export function write_root(manifest_data, output) { let l = max_depth; - let pyramid = ``; + let pyramid = ``; while (l--) { pyramid = ` {#if components[${l + 1}]} - + ${pyramid.replace(/\n/g, '\n\t\t\t\t\t')} {:else} - + {/if} ` .replace(/^\t\t\t/gm, '') @@ -49,7 +49,8 @@ export function write_root(manifest_data, output) { export let page; export let components; - ${levels.map((l) => `export let props_${l} = null;`).join('\n\t\t\t\t')} + ${levels.map((l) => `export let data_${l} = null;`).join('\n\t\t\t\t')} + export let errors; setContext('__svelte__', stores); diff --git a/packages/kit/src/core/sync/write_types.js b/packages/kit/src/core/sync/write_types.js index a543cc2047d0..633f48fc254c 100644 --- a/packages/kit/src/core/sync/write_types.js +++ b/packages/kit/src/core/sync/write_types.js @@ -1,81 +1,738 @@ -import { rimraf } from '../../utils/filesystem.js'; +import fs from 'fs'; +import path from 'path'; +import MagicString from 'magic-string'; +import { posixify, rimraf } from '../../utils/filesystem.js'; import { parse_route_id } from '../../utils/routing.js'; -import { write } from './utils.js'; +import { remove_from_previous, write_if_changed } from './utils.js'; -/** @param {string} imports */ -const header = (imports) => ` -// this file is auto-generated -import type { ${imports} } from '@sveltejs/kit';`; +/** + * @typedef { import('types').PageNode & { + * parent?: { + * key: string; + * name: string; + * folder_depth_diff: number; + * } + * } } Node + */ + +/** + * @typedef {{ + * leaf?: Node; + * default_layout?: Node; + * named_layouts: Map; + * endpoint?: string; + * }} NodeGroup + */ -/** @param {string} arg */ -const endpoint = (arg) => ` -export type RequestHandler = GenericRequestHandler<${arg}, Output>;`; +/** + * @typedef {{ + * modified: boolean; + * code: string; + * exports: any[]; + * } | null} Proxy + */ + +const cwd = process.cwd(); -/** @param {string} arg */ -const page = (arg) => ` -export type Load< - InputProps extends Record = Record, - OutputProps extends Record = InputProps -> = GenericLoad<${arg}, InputProps, OutputProps>;`; +const shared_names = new Set(['load']); +const server_names = new Set(['load', 'POST', 'PUT', 'PATCH', 'DELETE']); // TODO replace with a single `action` + +let first_run = true; /** + * Creates types for the whole manifest + * * @param {import('types').ValidatedConfig} config * @param {import('types').ManifestData} manifest_data */ -export function write_types(config, manifest_data) { - rimraf(`${config.kit.outDir}/types`); +export async function write_types(config, manifest_data) { + /** @type {import('typescript') | undefined} */ + let ts = undefined; + try { + ts = (await import('typescript')).default; + } catch (e) { + // No TypeScript installed - skip type generation + return; + } - /** @type {Map} */ - const shadow_types = new Map(); + const types_dir = `${config.kit.outDir}/types`; - manifest_data.routes.forEach((route) => { - const file = route.type === 'endpoint' ? route.file : route.shadow; + if (first_run) { + rimraf(types_dir); + first_run = false; + } - if (file) { - const ext = /** @type {string} */ ( - config.kit.moduleExtensions.find((ext) => file.endsWith(ext)) - ); - const key = file.slice(0, -ext.length); - shadow_types.set(key, { - params: parse_route_id(key).names, - type: route.type === 'endpoint' ? 'endpoint' : 'both' - }); + const routes_dir = posixify(path.relative('.', config.kit.files.routes)); + const groups = get_groups(manifest_data, routes_dir); + + let written_files = new Set(); + // ...then, for each directory, write $types.d.ts + for (const [dir] of groups) { + const written = write_types_for_dir(config, manifest_data, routes_dir, dir, groups, ts); + written.forEach((w) => written_files.add(w)); + } + + // Remove all files that were not updated, which means their original was removed + remove_from_previous((file) => { + const was_removed = file.startsWith(types_dir) && !written_files.has(file); + if (was_removed) { + rimraf(file); } + return was_removed; }); +} - manifest_data.components.forEach((component) => { - if (component.startsWith('.')) return; // exclude fallback components +/** + * Creates types related to the given file. This should only be called + * if the file in question was edited, not if it was created/deleted/moved. + * + * @param {import('types').ValidatedConfig} config + * @param {import('types').ManifestData} manifest_data + * @param {string} file + */ +export async function write_type(config, manifest_data, file) { + if (!path.basename(file).startsWith('+')) { + // Not a route file + return; + } - const ext = /** @type {string} */ (config.extensions.find((ext) => component.endsWith(ext))); - const key = component.slice(0, -ext.length); + /** @type {import('typescript') | undefined} */ + let ts = undefined; + try { + ts = (await import('typescript')).default; + } catch (e) { + // No TypeScript installed - skip type generation + return; + } - if (!shadow_types.has(key)) { - shadow_types.set(key, { params: parse_route_id(key).names, type: 'page' }); + const routes_dir = posixify(path.relative('.', config.kit.files.routes)); + const file_dir = posixify(path.dirname(file).slice(config.kit.files.routes.length + 1)); + const groups = get_groups(manifest_data, routes_dir); + + // We are only interested in the directory that contains the file + write_types_for_dir(config, manifest_data, routes_dir, file_dir, groups, ts); +} + +/** + * @param {import('types').ManifestData} manifest_data + * @param {string} routes_dir + */ +function get_groups(manifest_data, routes_dir) { + /** + * A map of all directories : route files. We don't just use + * manifest_data.routes, because that will exclude +layout + * files that aren't accompanied by a +page + * @type {Map} + */ + const groups = new Map(); + + /** @param {string} dir */ + function get_group(dir) { + let group = groups.get(dir); + if (!group) { + group = { + named_layouts: new Map() + }; + groups.set(dir, group); } - }); - shadow_types.forEach(({ params, type }, key) => { - const arg = - params.length > 0 ? `{ ${params.map((param) => `${param}: string`).join('; ')} }` : '{}'; + return group; + } + + // first, sort nodes by path length (necessary for finding the nearest layout more efficiently)... + const nodes = [...manifest_data.nodes].sort( + (n1, n2) => + /** @type {string} */ (n1.component ?? n1.shared ?? n1.server).split('/').length - + /** @type {string} */ (n2.component ?? n2.shared ?? n2.server).split('/').length + ); + + // ...then, populate `directories` with +page/+layout files... + for (let i = 0; i < nodes.length; i += 1) { + /** @type {Node} */ + const node = { ...nodes[i] }; // shallow copy so we don't mutate the original when setting parent + + // skip default layout/error + if (!node?.component?.startsWith(routes_dir)) continue; - const imports = []; - const content = []; + const parts = /** @type {string} */ (node.component ?? node.shared ?? node.server).split('/'); - if (type !== 'page') { - imports.push('RequestHandler as GenericRequestHandler, ResponseBody'); - content.push(endpoint(arg)); + const file = /** @type {string} */ (parts.pop()); + const dir = parts.join('/').slice(routes_dir.length + 1); + + // error pages don't need types + if (!file || file.startsWith('+error')) continue; + + const group = get_group(dir); + + if (file.startsWith('+page')) { + group.leaf = node; + } else { + const match = /^\+layout(?:-([^@.]+))?/.exec(file); + + // this shouldn't happen, but belt and braces. also keeps TS happy, + // and we live to keep TS happy + if (!match) throw new Error(`Unexpected route file: ${file}`); + + if (match[1]) { + group.named_layouts.set(match[1], node); + } else { + group.default_layout = node; + } } - if (type !== 'endpoint') { - imports.push('Load as GenericLoad'); - content.push(page(arg)); + node.parent = find_nearest_layout(routes_dir, nodes, i); + } + + // ...then add +server.js files... + for (const route of manifest_data.routes) { + if (route.type === 'endpoint') { + get_group(route.id).endpoint = route.file; } + } - content.unshift(header(imports.join(', '))); + return groups; +} - const parts = (key || 'index').split('/'); - parts.push('__types', /** @type {string} */ (parts.pop())); +/** + * + * @param {import('types').ValidatedConfig} config + * @param {import('types').ManifestData} manifest_data + * @param {string} routes_dir + * @param {string} dir + * @param {Map} groups + * @param {import('typescript')} ts + */ +function write_types_for_dir(config, manifest_data, routes_dir, dir, groups, ts) { + const group = groups.get(dir); + if (!group) { + return []; + } - write(`${config.kit.outDir}/types/${parts.join('/')}.d.ts`, content.join('\n').trim()); - }); + const outdir = `${config.kit.outDir}/types/${routes_dir}/${dir}`; + + const imports = [`import type * as Kit from '@sveltejs/kit';`]; + + /** @type {string[]} */ + const written_files = []; + + /** @type {string[]} */ + const declarations = []; + + /** @type {string[]} */ + const exports = []; + + const route_params = parse_route_id(dir).names; + + if (route_params.length > 0) { + const params = route_params.map((param) => `${param}: string`).join('; '); + declarations.push( + `interface RouteParams extends Partial> { ${params} }` + ); + } else { + declarations.push(`interface RouteParams extends Partial> {}`); + } + + if (group.leaf) { + const { data, server_data, load, server_load, errors, written_proxies } = process_node( + ts, + group.leaf, + outdir, + 'RouteParams', + groups + ); + written_files.push(...written_proxies); + + exports.push(`export type Errors = ${errors};`); + + exports.push(`export type PageData = ${data};`); + if (load) { + exports.push(`export type PageLoad = ${load};`); + } + + exports.push(`export type PageServerData = ${server_data};`); + if (server_load) { + exports.push(`export type PageServerLoad = ${server_load};`); + } + + if (group.leaf.server) { + exports.push(`export type Action = Kit.Action`); + } + } + + if (group.default_layout || group.named_layouts.size > 0) { + // TODO to be completely rigorous, we should have a LayoutParams per + // layout, and only include params for child pages that use each layout. + // but that's more work than i care to do right now + const layout_params = new Set(); + manifest_data.routes.forEach((route) => { + if (route.type === 'page' && route.id.startsWith(dir + '/')) { + // TODO this is O(n^2), see if we need to speed it up + for (const name of parse_route_id(route.id).names) { + layout_params.add(name); + } + } + }); + + if (layout_params.size > 0) { + const params = Array.from(layout_params).map((param) => `${param}?: string`); + declarations.push(`interface LayoutParams extends RouteParams { ${params.join('; ')} }`); + } else { + declarations.push(`interface LayoutParams extends RouteParams {}`); + } + + if (group.default_layout) { + const { data, server_data, load, server_load, written_proxies } = process_node( + ts, + group.default_layout, + outdir, + 'LayoutParams', + groups + ); + written_files.push(...written_proxies); + + exports.push(`export type LayoutData = ${data};`); + if (load) { + exports.push(`export type LayoutLoad = ${load};`); + } + + exports.push(`export type LayoutServerData = ${server_data};`); + if (server_load) { + exports.push(`export type LayoutServerLoad = ${server_load};`); + } + } + + if (group.named_layouts.size > 0) { + /** @type {string[]} */ + const data_exports = []; + + /** @type {string[]} */ + const server_data_exports = []; + + /** @type {string[]} */ + const load_exports = []; + + /** @type {string[]} */ + const server_load_exports = []; + + for (const [name, node] of group.named_layouts) { + const { data, server_data, load, server_load, written_proxies } = process_node( + ts, + node, + outdir, + 'LayoutParams', + groups + ); + written_files.push(...written_proxies); + data_exports.push(`export type ${name} = ${data};`); + server_data_exports.push(`export type ${name} = ${server_data};`); + if (load) { + load_exports.push(`export type ${name} = ${load};`); + } + if (server_load) { + server_load_exports.push(`export type ${name} = ${load};`); + } + } + + exports.push(`\nexport namespace LayoutData {\n\t${data_exports.join('\n\t')}\n}`); + exports.push(`\nexport namespace LayoutLoad {\n\t${load_exports.join('\n\t')}\n}`); + exports.push( + `\nexport namespace LayoutServerData {\n\t${server_data_exports.join('\n\t')}\n}` + ); + exports.push( + `\nexport namespace LayoutServerLoad {\n\t${server_load_exports.join('\n\t')}\n}` + ); + } + } + + if (group.endpoint) { + exports.push(`export type RequestHandler = Kit.RequestHandler;`); + } + + const output = [imports.join('\n'), declarations.join('\n'), exports.join('\n')] + .filter(Boolean) + .join('\n\n'); + + written_files.push(write(`${outdir}/$types.d.ts`, output)); + + return written_files; +} + +/** + * @param {import('typescript')} ts + * @param {Node} node + * @param {string} outdir + * @param {string} params + * @param {Map} groups + */ +function process_node(ts, node, outdir, params, groups) { + let data; + let load; + let server_load; + let errors; + + /** @type {string[]} */ + let written_proxies = []; + + let server_data; + + if (node.server) { + const content = fs.readFileSync(node.server, 'utf8'); + const proxy = tweak_types(ts, content, server_names); + const basename = path.basename(node.server); + if (proxy?.modified) { + written_proxies.push(write(`${outdir}/proxy${basename}`, proxy.code)); + } + + server_data = get_data_type(node.server, 'load', 'null', proxy); + server_load = `Kit.ServerLoad<${params}, ${get_parent_type('LayoutServerData')}>`; + + if (proxy) { + const types = []; + for (const method of ['POST', 'PUT', 'PATCH']) { + if (proxy.exports.includes(method)) { + if (proxy.modified) { + types.push(`Kit.AwaitedErrors`); + } else { + // If the file wasn't tweaked, we can use the return type of the original file. + // The advantage is that type updates are reflected without saving. + types.push( + `Kit.AwaitedErrors` + ); + } + } + } + errors = types.length ? types.join(' | ') : 'null'; + } else { + errors = 'unknown'; + } + } else { + server_data = 'null'; + } + + if (node.shared) { + const content = fs.readFileSync(node.shared, 'utf8'); + const proxy = tweak_types(ts, content, shared_names); + if (proxy?.modified) { + written_proxies.push(write(`${outdir}/proxy${path.basename(node.shared)}`, proxy.code)); + } + + data = get_data_type(node.shared, 'load', server_data, proxy); + load = `Kit.Load<${params}, ${server_data}, ${get_parent_type('LayoutData')}>`; + } else { + data = server_data; + } + + return { data, server_data, load, server_load, errors, written_proxies }; + + /** + * @param {string} file_path + * @param {string} method + * @param {string} fallback + * @param {Proxy} proxy + */ + function get_data_type(file_path, method, fallback, proxy) { + if (proxy) { + if (proxy.exports.includes(method)) { + if (proxy.modified) { + const basename = path.basename(file_path); + return `Kit.AwaitedProperties>>`; + } else { + // If the file wasn't tweaked, we can use the return type of the original file. + // The advantage is that type updates are reflected without saving. + return `Kit.AwaitedProperties>>`; + } + } else { + return fallback; + } + } else { + return 'unknown'; + } + } + + /** + * Get the parent type string by recursively looking up the parent layout and accumulate them to one type. + * @param {string} type + */ + function get_parent_type(type) { + const parent_imports = []; + let parent = node.parent; + let acc_diff = 0; + + while (parent) { + acc_diff += parent.folder_depth_diff; + let parent_group = /** @type {NodeGroup} */ (groups.get(parent.key)); + // unshift because we need it the other way round for the import string + parent_imports.unshift( + (acc_diff === 0 ? '' : `import('` + '../'.repeat(acc_diff) + '$types.js' + `').`) + + `${type}${parent.name ? `.${parent.name}` : ''}` + ); + let parent_layout = /** @type {Node} */ ( + parent.name ? parent_group.named_layouts.get(parent.name) : parent_group.default_layout + ); + parent = parent_layout.parent; + } + + let parent_str = parent_imports[0] || 'null'; + for (let i = 1; i < parent_imports.length; i++) { + // Omit is necessary because a parent could have a property with the same key which would + // cause a type conflict. At runtime the child overwrites the parent property in this case, + // so reflect that in the type definition. + parent_str = `Omit<${parent_str}, keyof ${parent_imports[i]}> & ${parent_imports[i]}`; + } + return parent_str; + } +} + +/** + * @param {string} outdir + * @param {string} file_path + */ +function path_to_original(outdir, file_path) { + const ext = path.extname(file_path); + return posixify( + path.relative( + outdir, + path.join( + cwd, + // Another extension than `.js` (or nothing, but that fails with node16 moduleResolution) + // will result in TS failing to lookup the file + file_path.slice(0, -ext.length) + '.js' + ) + ) + ); +} + +/** + * @param {import('typescript')} ts + * @param {string} content + * @param {Set} names + * @returns {Proxy} + */ +export function tweak_types(ts, content, names) { + try { + let modified = false; + + const ast = ts.createSourceFile( + 'filename.ts', + content, + ts.ScriptTarget.Latest, + false, + ts.ScriptKind.TS + ); + + const code = new MagicString(content); + + const exports = new Map(); + + ast.forEachChild((node) => { + if ( + ts.isExportDeclaration(node) && + node.exportClause && + ts.isNamedExports(node.exportClause) + ) { + node.exportClause.elements.forEach((element) => { + const exported = element.name; + if (names.has(element.name.text)) { + const local = element.propertyName || element.name; + exports.set(exported.text, local.text); + } + }); + } + + if (node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) { + if (ts.isFunctionDeclaration(node) && node.name?.text && names.has(node.name?.text)) { + exports.set(node.name.text, node.name.text); + } + + if (ts.isVariableStatement(node)) { + node.declarationList.declarations.forEach((declaration) => { + if (ts.isIdentifier(declaration.name) && names.has(declaration.name.text)) { + exports.set(declaration.name.text, declaration.name.text); + } + }); + } + } + }); + + /** + * @param {import('typescript').Node} node + * @param {import('typescript').Node} value + */ + function replace_jsdoc_type_tags(node, value) { + // @ts-ignore + if (node.jsDoc) { + // @ts-ignore + for (const comment of node.jsDoc) { + for (const tag of comment.tags) { + if (ts.isJSDocTypeTag(tag)) { + const is_fn = + ts.isFunctionDeclaration(value) || + ts.isFunctionExpression(value) || + ts.isArrowFunction(value); + + if (is_fn && value.parameters?.length > 0) { + code.overwrite(tag.tagName.pos, tag.tagName.end, 'param'); + code.prependRight(tag.typeExpression.pos + 1, 'Parameters<'); + code.appendLeft(tag.typeExpression.end - 1, '>[0]'); + code.appendLeft(tag.typeExpression.end, ' event'); + } else { + code.overwrite(tag.pos, tag.end, ''); + } + modified = true; + } + } + } + } + } + + ast.forEachChild((node) => { + if (ts.isFunctionDeclaration(node) && node.name?.text && names.has(node.name?.text)) { + // remove JSDoc comment above `export function load ...` + replace_jsdoc_type_tags(node, node); + } + + if (ts.isVariableStatement(node)) { + // remove JSDoc comment above `export const load = ...` + if ( + ts.isIdentifier(node.declarationList.declarations[0].name) && + names.has(node.declarationList.declarations[0].name.text) && + node.declarationList.declarations[0].initializer + ) { + replace_jsdoc_type_tags(node, node.declarationList.declarations[0].initializer); + } + + for (const declaration of node.declarationList.declarations) { + if ( + ts.isIdentifier(declaration.name) && + names.has(declaration.name.text) && + declaration.initializer + ) { + // edge case — remove JSDoc comment above individual export + replace_jsdoc_type_tags(declaration, declaration.initializer); + + // remove type from `export const load: Load ...` + if (declaration.type) { + let a = declaration.type.pos; + let b = declaration.type.end; + while (/\s/.test(content[a])) a += 1; + + const type = content.slice(a, b); + code.remove(declaration.name.end, declaration.type.end); + + const rhs = declaration.initializer; + + if (rhs && (ts.isArrowFunction(rhs) || ts.isFunctionExpression(rhs))) { + const arg = rhs.parameters[0]; + + const add_parens = content[arg.pos - 1] !== '('; + + if (add_parens) code.prependRight(arg.pos, '('); + + if (arg && !arg.type) { + code.appendLeft( + arg.name.end, + `: Parameters<${type}>[0]` + (add_parens ? ')' : '') + ); + } + } + + modified = true; + } + } + } + } + }); + + return { + modified, + code: code.toString(), + exports: Array.from(exports.keys()) + }; + } catch { + return null; + } +} + +/** + * @param {string} file + * @param {string} content + */ +function write(file, content) { + write_if_changed(file, content); + return file; +} + +/** + * Finds the nearest layout for given node. + * Assumes that nodes is sorted by path length (lowest first). + * + * @param {string} routes_dir + * @param {import('types').PageNode[]} nodes + * @param {number} start_idx + */ +export function find_nearest_layout(routes_dir, nodes, start_idx) { + const start_file = /** @type {string} */ ( + nodes[start_idx].component || nodes[start_idx].shared || nodes[start_idx].server + ); + + let name = ''; + const match = /^\+(layout|page)(?:-([^@.]+))?(?:@([^@.]+))?/.exec(path.basename(start_file)); + if (!match) throw new Error(`Unexpected route file: ${start_file}`); + if (match[3] && match[3] !== 'default') { + name = match[3]; // a named layout is referenced + } + + let common_path = path.dirname(start_file); + if (match[1] === 'layout' && !name) { + // We are a default layout, so we skip the current level + common_path = path.dirname(common_path); + } + + for (let i = start_idx; i >= 0; i -= 1) { + const node = nodes[i]; + const file = /** @type {string} */ (node.component || node.shared || node.server); + + const current_path = path.dirname(file); + const common_path_length = common_path.split('/').length; + const current_path_length = current_path.split('/').length; + + if (common_path_length < current_path_length) { + // this is a layout in a different tree + continue; + } else if (common_path_length > current_path_length) { + // we've gone back up a folder level + common_path = path.dirname(common_path); + } + if (common_path !== current_path) { + // this is a layout in a different tree + continue; + } + if ( + path.basename(file, path.extname(file)).split('@')[0] !== + '+layout' + (name ? `-${name}` : '') + ) { + // this is not the layout we are searching for + continue; + } + + // matching parent layout found + // let import_path = posixify(path.relative(path.dirname(start_file), common_path + '/$types.js')); + // if (!import_path.startsWith('.')) { + // import_path = './' + import_path; + // } + let folder_depth_diff = + posixify(path.relative(path.dirname(start_file), common_path + '/$types.js')).split('/') + .length - 1; + return { + key: path.dirname(file).slice(routes_dir.length + 1), + name, + folder_depth_diff + }; + } } diff --git a/packages/kit/src/core/sync/write_types.spec.js b/packages/kit/src/core/sync/write_types.spec.js new file mode 100644 index 000000000000..e614e18783ef --- /dev/null +++ b/packages/kit/src/core/sync/write_types.spec.js @@ -0,0 +1,147 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import ts from 'typescript'; +import { find_nearest_layout, tweak_types } from './write_types.js'; + +test('Rewrites types for a TypeScript module', () => { + const source = ` + export const GET: Get = ({ params }) => { + return { + a: 1 + }; + }; + `; + + const rewritten = tweak_types(ts, source, new Set(['GET'])); + + assert.equal(rewritten?.exports, ['GET']); + assert.equal( + rewritten?.code, + ` + export const GET = ({ params }: Parameters[0]) => { + return { + a: 1 + }; + }; + ` + ); +}); + +test('Rewrites types for a JavaScript module with `function`', () => { + const source = ` + /** @type {import('./$types').Get} */ + export function GET({ params }) { + return { + a: 1 + }; + }; + `; + + const rewritten = tweak_types(ts, source, new Set(['GET'])); + + assert.equal(rewritten?.exports, ['GET']); + assert.equal( + rewritten?.code, + ` + /** @param {Parameters[0]} event */ + export function GET({ params }) { + return { + a: 1 + }; + }; + ` + ); +}); + +test('Rewrites types for a JavaScript module with `const`', () => { + const source = ` + /** @type {import('./$types').Get} */ + export const GET = ({ params }) => { + return { + a: 1 + }; + }; + `; + + const rewritten = tweak_types(ts, source, new Set(['GET'])); + + assert.equal(rewritten?.exports, ['GET']); + assert.equal( + rewritten?.code, + ` + /** @param {Parameters[0]} event */ + export const GET = ({ params }) => { + return { + a: 1 + }; + }; + ` + ); +}); + +/** @type {import('types').PageNode[]} */ +const nodes = [ + { component: 'src/routes/+layout.svelte' }, // 0 + { component: 'src/routes/+layout-named2@default.svelte' }, // 1 + { component: 'src/routes/+layout-named.svelte' }, // 2 + { + shared: 'src/routes/+page.js', + component: 'src/routes/+page@named2.svelte' + }, // 3 + { + server: 'src/routes/docs/+layout.server.js', + component: 'src/routes/docs/+layout.svelte' + }, // 4 + { shared: 'src/routes/docs/+page.js' }, // 5 + { + server: 'src/routes/faq/+page.server.js', + component: 'src/routes/faq/+page.svelte' + }, // 6 + { + shared: 'src/routes/search/+page.js', + server: 'src/routes/search/+page.server.js', + component: 'src/routes/search/+page.svelte' + }, // 7 + { + server: 'src/routes/docs/[slug]/+page.server.js', + component: 'src/routes/docs/[slug]/+page.svelte' + }, // 8 + { + server: 'src/routes/docs/[slug]/hi/+page.server.js', + component: 'src/routes/docs/[slug]/hi/+page@named.svelte' + } // 9 +]; + +test('Finds nearest layout (nested)', () => { + assert.equal(find_nearest_layout('src/routes', nodes, 8), { + key: 'docs', + folder_depth_diff: 1, + name: '' + }); +}); + +test('Finds nearest layout (root)', () => { + assert.equal(find_nearest_layout('src/routes', nodes, 6), { + key: '', + folder_depth_diff: 1, + name: '' + }); +}); + +test('Finds nearest layout (named)', () => { + assert.equal(find_nearest_layout('src/routes', nodes, 9), { + key: '', + folder_depth_diff: 3, + name: 'named' + }); +}); + +test('Finds nearest layout (recursively named)', () => { + assert.equal(find_nearest_layout('src/routes', nodes, 3), { + key: '', + folder_depth_diff: 0, + name: 'named2' + }); +}); + +test.run(); diff --git a/packages/kit/src/core/utils.js b/packages/kit/src/core/utils.js index c60e5d92bdb3..14c85f20ec91 100644 --- a/packages/kit/src/core/utils.js +++ b/packages/kit/src/core/utils.js @@ -1,30 +1,20 @@ import path from 'path'; import colors from 'kleur'; import { fileURLToPath } from 'url'; +import { posixify } from '../utils/filesystem.js'; /** - * Get the prefix for the `runtime` directory, for use with import declarations - * @param {import('types').ValidatedKitConfig} config + * Resolved path of the `runtime` directory + * + * TODO Windows issue: + * Vite or sth else somehow sets the driver letter inconsistently to lower or upper case depending on the run environment. + * In playwright debug mode run through VS Code this a root-to-lowercase conversion is needed in order for the tests to run. + * If we do this conversion in other cases it has the opposite effect though and fails. */ -export function get_runtime_prefix(config) { - if (process.env.BUNDLED) { - return posixify_path(path.join(config.outDir, 'runtime')); - } +export const runtime_directory = posixify(fileURLToPath(new URL('../runtime', import.meta.url))); - return posixify_path(fileURLToPath(new URL('../runtime', import.meta.url))); -} - -/** - * Get the resolved path of the `runtime` directory - * @param {import('types').ValidatedKitConfig} config - */ -export function get_runtime_directory(config) { - if (process.env.BUNDLED) { - return path.join(config.outDir, 'runtime'); - } - - return fileURLToPath(new URL('../runtime', import.meta.url)); -} +/** Prefix for the `runtime` directory, for use with import declarations */ +export const runtime_prefix = posixify_path(runtime_directory); /** @param {string} str */ function posixify_path(str) { diff --git a/packages/kit/src/index/index.js b/packages/kit/src/index/index.js new file mode 100644 index 000000000000..715cbae45894 --- /dev/null +++ b/packages/kit/src/index/index.js @@ -0,0 +1,45 @@ +import { HttpError, Redirect } from './private.js'; + +/** + * Creates an `HttpError` object with an HTTP status code and an optional message. + * This object, if thrown during request handling, will cause SvelteKit to + * return an error response without invoking `handleError` + * @param {number} status + * @param {string | undefined} [message] + */ +export function error(status, message) { + return new HttpError(status, message); +} + +/** + * Creates a `Redirect` object. If thrown during request handling, SvelteKit will + * return a redirect response. + * @param {number} status + * @param {string} location + */ +export function redirect(status, location) { + if (isNaN(status) || status < 300 || status > 399) { + throw new Error('Invalid status code'); + } + + return new Redirect(status, location); +} + +/** + * Generates a JSON `Response` object from the supplied data. + * @param {any} data + * @param {ResponseInit} [init] + */ +export function json(data, init) { + // TODO deprecate this in favour of `Response.json` when it's + // more widely supported + const headers = new Headers(init?.headers); + if (!headers.has('content-type')) { + headers.set('content-type', 'application/json'); + } + + return new Response(JSON.stringify(data), { + ...init, + headers + }); +} diff --git a/packages/kit/src/index/private.js b/packages/kit/src/index/private.js new file mode 100644 index 000000000000..e2f45c5f165d --- /dev/null +++ b/packages/kit/src/index/private.js @@ -0,0 +1,31 @@ +export class HttpError { + // without these, things like `$page.error.stack` will error. we don't want to + // include a stack for these sorts of errors, but we also don't want red + // squigglies everywhere, so this feels like a not-terribile compromise + name = 'HttpError'; + stack = undefined; + + /** + * @param {number} status + * @param {string | undefined} message + */ + constructor(status, message) { + this.status = status; + this.message = message ?? `Error: ${status}`; + } + + toString() { + return this.message; + } +} + +export class Redirect { + /** + * @param {number} status + * @param {string} location + */ + constructor(status, location) { + this.status = status; + this.location = location; + } +} diff --git a/packages/kit/src/runtime/client/ambient.d.ts b/packages/kit/src/runtime/client/ambient.d.ts index 5f86e2fce7ef..7560947d5fb1 100644 --- a/packages/kit/src/runtime/client/ambient.d.ts +++ b/packages/kit/src/runtime/client/ambient.d.ts @@ -1,16 +1,17 @@ declare module '__GENERATED__/client-manifest.js' { - import { CSRComponentLoader, ParamMatcher } from 'types'; + import { CSRPageNodeLoader, ParamMatcher } from 'types'; /** - * A list of all the layout/pages components used in the app + * A list of all the error/layout/page nodes used in the app */ - export const components: CSRComponentLoader[]; + export const nodes: CSRPageNodeLoader[]; /** - * A map of `[routeId: string]: [a, b, has_endpoint]` tuples, which - * is parsed into an array of routes on startup + * A map of `[routeId: string]: [errors, layouts, page]` tuples, which + * is parsed into an array of routes on startup. The numbers refer to the + * indices in `nodes`. */ - export const dictionary: Record; + export const dictionary: Record; export const matchers: Record; } diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index 5f1db7986e7e..77bad23c532f 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -1,7 +1,6 @@ import { onMount, tick } from 'svelte'; import { writable } from 'svelte/store'; -import { coalesce_to_error } from '../../utils/error.js'; -import { normalize } from '../load.js'; +import { normalize_error } from '../../utils/error.js'; import { LoadURL, decode_params, normalize_path } from '../../utils/url.js'; import { create_updated_store, @@ -13,21 +12,21 @@ import { } from './utils.js'; import { lock_fetch, unlock_fetch, initial_fetch, native_fetch } from './fetcher.js'; import { parse } from './parse.js'; +import { error } from '../../index/index.js'; import Root from '__GENERATED__/root.svelte'; -import { components, dictionary, matchers } from '__GENERATED__/client-manifest.js'; +import { nodes, dictionary, matchers } from '__GENERATED__/client-manifest.js'; +import { HttpError, Redirect } from '../../index/private.js'; const SCROLL_KEY = 'sveltekit:scroll'; const INDEX_KEY = 'sveltekit:index'; -const routes = parse(components, dictionary, matchers); +const routes = parse(nodes, dictionary, matchers); -// we import the root layout/error components eagerly, so that +// we import the root layout/error nodes eagerly, so that // connectivity errors after initialisation don't nuke the app -const default_layout = components[0](); -const default_error = components[1](); - -const root_stuff = {}; +const default_layout = nodes[0](); +const default_error = nodes[1](); // We track the scroll position associated with each history entry in sessionStorage, // rather than on history.state itself, because when navigation is driven by @@ -58,9 +57,6 @@ function update_scroll_positions(index) { * @returns {import('./types').Client} */ export function create_client({ target, session, base, trailing_slash }) { - /** @type {Map} */ - const cache = new Map(); - /** @type {Array<((href: string) => boolean)>} */ const invalidated = []; @@ -91,7 +87,6 @@ export function create_client({ target, session, base, trailing_slash }) { branch: [], error: null, session_id: 0, - stuff: root_stuff, // @ts-ignore - we need the initial value to be null url: null }; @@ -120,7 +115,7 @@ export function create_client({ target, session, base, trailing_slash }) { const current_load_uses_session = current.branch.some((node) => node?.uses.session); if (!current_load_uses_session) return; - update(new URL(location.href), [], true); + update(new URL(location.href), []); }); ready = true; @@ -198,7 +193,7 @@ export function create_client({ target, session, base, trailing_slash }) { throw new Error('Attempted to prefetch a URL that does not belong to this app'); } - load_cache.promise = load_route(intent, false); + load_cache.promise = load_route(intent); load_cache.id = intent.id; return load_cache.promise; @@ -208,15 +203,14 @@ export function create_client({ target, session, base, trailing_slash }) { * Returns `true` if update completes, `false` if it is aborted * @param {URL} url * @param {string[]} redirect_chain - * @param {boolean} no_cache * @param {{hash?: string, scroll: { x: number, y: number } | null, keepfocus: boolean, details: { replaceState: boolean, state: any } | null}} [opts] * @param {() => void} [callback] */ - async function update(url, redirect_chain, no_cache, opts, callback) { + async function update(url, redirect_chain, opts, callback) { const intent = get_navigation_intent(url); const current_token = (token = {}); - let navigation_result = intent && (await load_route(intent, no_cache)); + let navigation_result = intent && (await load_route(intent)); if ( !navigation_result && @@ -250,7 +244,7 @@ export function create_client({ target, session, base, trailing_slash }) { invalidated.length = 0; - if (navigation_result.redirect) { + if (navigation_result.type === 'redirect') { if (redirect_chain.length > 10 || redirect_chain.includes(url.pathname)) { navigation_result = await load_root_error_page({ status: 500, @@ -260,12 +254,12 @@ export function create_client({ target, session, base, trailing_slash }) { }); } else { if (router_enabled) { - goto(new URL(navigation_result.redirect, url).href, {}, [ + goto(new URL(navigation_result.location, url).href, {}, [ ...redirect_chain, url.pathname ]); } else { - await native_navigation(new URL(navigation_result.redirect, location.href)); + await native_navigation(new URL(navigation_result.location, location.href)); } return false; @@ -355,15 +349,15 @@ export function create_client({ target, session, base, trailing_slash }) { page = navigation_result.props.page; } - const leaf_node = navigation_result.state.branch[navigation_result.state.branch.length - 1]; - router_enabled = leaf_node?.module.router !== false; + const leaf_node = navigation_result.state.branch.at(-1); + router_enabled = leaf_node?.node.shared?.router !== false; if (callback) callback(); updating = false; } - /** @param {import('./types').NavigationResult} result */ + /** @param {import('./types').NavigationFinished} result */ function initialize(result) { current = result.state; @@ -391,57 +385,53 @@ export function create_client({ target, session, base, trailing_slash }) { * @param {{ * url: URL; * params: Record; - * stuff: Record; * branch: Array; * status: number; - * error: Error | null; + * error: HttpError | Error | null; * routeId: string | null; * }} opts */ async function get_navigation_result_from_branch({ url, params, - stuff, branch, status, error, routeId }) { const filtered = /** @type {import('./types').BranchNode[] } */ (branch.filter(Boolean)); - const redirect = filtered.find((f) => f.loaded?.redirect); - /** @type {import('./types').NavigationResult} */ + /** @type {import('./types').NavigationFinished} */ const result = { - redirect: redirect?.loaded?.redirect, + type: 'loaded', state: { url, params, branch, error, - stuff, session_id }, props: { - components: filtered.map((node) => node.module.default) + components: filtered.map((branch_node) => branch_node.node.component) } }; + let data = {}; + let data_changed = false; for (let i = 0; i < filtered.length; i += 1) { + Object.assign(data, filtered[i].data); // Only set props if the node actually updated. This prevents needless rerenders. if (!current.branch.some((node) => node === filtered[i])) { - const loaded = filtered[i].loaded; - result.props[`props_${i}`] = loaded ? await loaded.props : null; + result.props[`data_${i}`] = filtered[i].data; + data_changed = true; } } const page_changed = - !current.url || - url.href !== current.url.href || - current.error !== error || - current.stuff !== stuff; + !current.url || url.href !== current.url.href || current.error !== error || data_changed; if (page_changed) { - result.props.page = { error, params, routeId, status, stuff, url }; + result.props.page = { error, params, routeId, status, url, data }; // TODO remove this for 1.0 /** @@ -461,72 +451,49 @@ export function create_client({ target, session, base, trailing_slash }) { print_error('query', 'searchParams'); } - const leaf = filtered[filtered.length - 1]; - const load_cache = leaf?.loaded?.cache; - - if (load_cache) { - const key = url.pathname + url.search; // omit hash - let ready = false; - - const clear = () => { - if (cache.get(key) === result) { - cache.delete(key); - } - - unsubscribe(); - clearTimeout(timeout); - }; - - const timeout = setTimeout(clear, load_cache.maxage * 1000); - - const unsubscribe = stores.session.subscribe(() => { - if (ready) clear(); - }); - - ready = true; - - cache.set(key, result); - } - return result; } /** + * Call the load function of the given node, if it exists. + * If `server_data` is passed, this is treated as the initial run and the page endpoint is not requested. + * * @param {{ - * status?: number; - * error?: Error; - * module: import('types').CSRComponent; + * node: import('types').CSRPageNode; + * parent: () => Promise>; * url: URL; * params: Record; - * stuff: Record; - * props?: Record; * routeId: string | null; + * server_data: import('types').JSONObject | null; * }} options + * @returns {Promise} */ - async function load_node({ status, error, module, url, params, stuff, props, routeId }) { - /** @type {import('./types').BranchNode} */ - const node = { - module, - uses: { - params: new Set(), - url: false, - session: false, - stuff: false, - dependencies: new Set() - }, - loaded: null, - stuff + async function load_node({ node, parent, url, params, routeId, server_data }) { + const uses = { + params: new Set(), + url: false, + session: false, + dependencies: new Set(), + parent: false }; - /** @param dep {string} */ - function add_dependency(dep) { - const { href } = new URL(dep, url); - node.uses.dependencies.add(href); + /** @param {string[]} deps */ + function depends(...deps) { + for (const dep of deps) { + const { href } = new URL(dep, url); + uses.dependencies.add(href); + } } - if (props) { - // shadow endpoint props means we need to mark this URL as a dependency of itself - node.uses.dependencies.add(url.href); + /** @type {Record | null} */ + let data = null; + + if (node.server) { + // +page|layout.server.js data means we need to mark this URL as a dependency of itself, + // unless we want to get clever with usage detection on the server, which could + // be returned to the client either as payload or custom headers + uses.dependencies.add(url.href); + uses.url = true; } /** @type {Record} */ @@ -534,7 +501,7 @@ export function create_client({ target, session, base, trailing_slash }) { for (const key in params) { Object.defineProperty(uses_params, key, { get() { - node.uses.params.add(key); + uses.params.add(key); return params[key]; }, enumerable: true @@ -544,24 +511,20 @@ export function create_client({ target, session, base, trailing_slash }) { const session = $session; const load_url = new LoadURL(url); - if (module.load) { + if (node.shared?.load) { /** @type {import('types').LoadEvent} */ const load_input = { routeId, params: uses_params, - props: props || {}, + data: server_data, get url() { - node.uses.url = true; + uses.url = true; return load_url; }, get session() { - node.uses.session = true; + uses.session = true; return session; }, - get stuff() { - node.uses.stuff = true; - return { ...stuff }; - }, async fetch(resource, init) { let requested; @@ -596,61 +559,61 @@ export function create_client({ target, session, base, trailing_slash }) { // we must fixup relative urls so they are resolved from the target page const normalized = new URL(requested, url).href; - add_dependency(normalized); + depends(normalized); // prerendered pages may be served from any origin, so `initial_fetch` urls shouldn't be normalized return started ? native_fetch(normalized, init) : initial_fetch(requested, init); }, - status: status ?? null, - error: error ?? null + setHeaders: () => {}, // noop + depends, + get parent() { + // uses.parent assignment here, not on method inokation, else we wouldn't notice when someone + // does await parent() inside an if branch which wasn't executed yet. + uses.parent = true; + return parent; + }, + // @ts-expect-error + get props() { + throw new Error( + '@migration task: Replace `props` with `data` stuff https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292693' + ); + }, + get stuff() { + throw new Error( + '@migration task: Remove stuff https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292693' + ); + } }; - if (import.meta.env.DEV) { - // TODO remove this for 1.0 - Object.defineProperty(load_input, 'page', { - get: () => { - throw new Error('`page` in `load` functions has been replaced by `url` and `params`'); - } - }); - } - if (import.meta.env.DEV) { try { lock_fetch(); - node.loaded = normalize(await module.load.call(null, load_input)); + data = (await node.shared.load.call(null, load_input)) ?? null; } finally { unlock_fetch(); } } else { - node.loaded = normalize(await module.load.call(null, load_input)); - } - - if (node.loaded.stuff) node.stuff = node.loaded.stuff; - if (node.loaded.dependencies) { - node.loaded.dependencies.forEach(add_dependency); + data = (await node.shared.load.call(null, load_input)) ?? null; } - } else if (props) { - node.loaded = normalize({ props }); } - return node; + return { + node, + data: data || server_data, + uses + }; } /** * @param {import('./types').NavigationIntent} intent - * @param {boolean} no_cache + * @returns {Promise} */ - async function load_route({ id, url, params, route }, no_cache) { + async function load_route({ id, url, params, route }) { if (load_cache.id === id && load_cache.promise) { return load_cache.promise; } - if (!no_cache) { - const cached = cache.get(id); - if (cached) return cached; - } - - const { a, b, has_shadow } = route; + const { errors, layouts, leaf } = route; const changed = current.url && { url: id !== current.url.pathname + current.url.search, @@ -658,191 +621,175 @@ export function create_client({ target, session, base, trailing_slash }) { session: session_id !== current.session_id }; - /** @type {Array} */ - let branch = []; - - /** @type {Record} */ - let stuff = root_stuff; - let stuff_changed = false; - - /** @type {number} */ - let status = 200; - - /** @type {Error | null} */ - let error = null; - // preload modules to avoid waterfall, but handle rejections // so they don't get reported to Sentry et al (we don't need // to act on the failures at this point) - a.forEach((loader) => loader().catch(() => {})); - - load: for (let i = 0; i < a.length; i += 1) { - /** @type {import('./types').BranchNode | undefined} */ - let node; + [...errors, ...layouts, leaf].forEach((loader) => loader?.().catch(() => {})); - try { - if (!a[i]) continue; + const nodes = [...layouts, leaf]; - const module = await a[i](); + // To avoid waterfalls when someone awaits a parent, compute as much as possible here already + /** @type {boolean[]} */ + const nodes_changed_since_last_render = []; + for (let i = 0; i < nodes.length; i++) { + if (!nodes[i]) { + nodes_changed_since_last_render.push(false); + } else { const previous = current.branch[i]; - const changed_since_last_render = !previous || - module !== previous.module || (changed.url && previous.uses.url) || changed.params.some((param) => previous.uses.params.has(param)) || (changed.session && previous.uses.session) || Array.from(previous.uses.dependencies).some((dep) => invalidated.some((fn) => fn(dep))) || - (stuff_changed && previous.uses.stuff); + (previous.uses.parent && nodes_changed_since_last_render.includes(true)); + nodes_changed_since_last_render.push(changed_since_last_render); + } + } - if (changed_since_last_render) { - /** @type {Record} */ - let props = {}; - - const is_shadow_page = has_shadow && i === a.length - 1; - - if (is_shadow_page) { - const res = await native_fetch( - `${url.pathname}${url.pathname.endsWith('/') ? '' : '/'}__data.json${url.search}`, - { - headers: { - 'x-sveltekit-load': 'true' - } - } - ); + /** @type {import('./types').ServerDataPayload | null} */ + let server_data_payload = null; - if (res.ok) { - const redirect = res.headers.get('x-sveltekit-location'); + if (route.uses_server_data) { + try { + const res = await native_fetch( + `${url.pathname}${url.pathname.endsWith('/') ? '' : '/'}__data.json${url.search}` + ); - if (redirect) { - return { - redirect, - props: {}, - state: current - }; - } + server_data_payload = /** @type {import('./types').ServerDataPayload} */ (await res.json()); - props = res.status === 204 ? {} : await res.json(); - } else { - status = res.status; - try { - error = await res.json(); - } catch (e) { - error = new Error('Failed to load data'); - } - } - } + if (!res.ok) { + throw server_data_payload; + } + } catch (e) { + throw new Error('TODO render fallback error page'); + } - if (!error) { - node = await load_node({ - module, - url, - params, - props, - stuff, - routeId: route.id - }); - } + if (server_data_payload.type === 'redirect') { + return server_data_payload; + } + } - if (node) { - if (is_shadow_page) { - node.uses.url = true; - } + const server_data_nodes = server_data_payload?.nodes; - if (node.loaded) { - if (node.loaded.error) { - status = node.loaded.status ?? 500; - error = node.loaded.error; - } + const branch_promises = nodes.map(async (loader, i) => { + return Promise.resolve().then(async () => { + if (!loader) return; + const node = await loader(); - if (node.loaded.redirect) { - return { - redirect: node.loaded.redirect, - props: {}, - state: current - }; - } + /** @type {import('./types').BranchNode | undefined} */ + const previous = current.branch[i]; + const changed_since_last_render = + nodes_changed_since_last_render[i] || !previous || node !== previous.node; - if (node.loaded.stuff) { - stuff_changed = true; - } - } + if (changed_since_last_render) { + const payload = server_data_nodes?.[i]; + + if (payload?.status) { + throw error(payload.status, payload.message); } + + if (payload?.error) { + throw payload.error; + } + + return await load_node({ + node, + url, + params, + routeId: route.id, + parent: async () => { + const data = {}; + for (let j = 0; j < i; j += 1) { + Object.assign(data, (await branch_promises[j])?.data); + } + return data; + }, + server_data: payload?.data ?? null + }); } else { - node = previous; + return previous; } - } catch (e) { - status = 500; - error = coalesce_to_error(e); - } + }); + }); - if (error) { - while (i--) { - if (b[i]) { - let error_loaded; + // if we don't do this, rejections will be unhandled + for (const p of branch_promises) p.catch(() => {}); - /** @type {import('./types').BranchNode | undefined} */ - let node_loaded; - let j = i; - while (!(node_loaded = branch[j])) { - j -= 1; - } + /** @type {Array} */ + const branch = []; - try { - error_loaded = await load_node({ - status, - error, - module: await b[i](), - url, - params, - stuff: node_loaded.stuff, - routeId: route.id - }); - - if (error_loaded?.loaded?.error) { - continue; - } + for (let i = 0; i < nodes.length; i += 1) { + if (nodes[i]) { + try { + branch.push(await branch_promises[i]); + } catch (e) { + const error = normalize_error(e); + + if (error instanceof Redirect) { + return { + type: 'redirect', + location: error.location + }; + } + + const status = e instanceof HttpError ? e.status : 500; + + while (i--) { + if (errors[i]) { + /** @type {import('./types').BranchNode | undefined} */ + let error_loaded; - if (error_loaded?.loaded?.stuff) { - stuff = { - ...stuff, - ...error_loaded.loaded.stuff + let j = i; + while (!branch[j]) j -= 1; + + try { + error_loaded = { + node: await errors[i](), + data: {}, + uses: { + params: new Set(), + url: false, + session: false, + dependencies: new Set(), + parent: false + } }; - } - branch = branch.slice(0, j + 1).concat(error_loaded); - break load; - } catch (e) { - continue; + return await get_navigation_result_from_branch({ + url, + params, + branch: branch.slice(0, j + 1).concat(error_loaded), + status, + error, + routeId: route.id + }); + } catch (e) { + continue; + } } } - } - return await load_root_error_page({ - status, - error, - url, - routeId: route.id - }); - } else { - if (node?.loaded?.stuff) { - stuff = { - ...stuff, - ...node.loaded.stuff - }; + return await load_root_error_page({ + status, + error, + url, + routeId: route.id + }); } - - branch.push(node); + } else { + // push an empty slot so we can rewind past gaps to the + // layout that corresponds with an +error.svelte page + branch.push(undefined); } } return await get_navigation_result_from_branch({ url, params, - stuff, branch, - status, - error, + status: 200, + error: null, routeId: route.id }); } @@ -850,7 +797,7 @@ export function create_client({ target, session, base, trailing_slash }) { /** * @param {{ * status: number; - * error: Error; + * error: HttpError | Error; * url: URL; * routeId: string | null * }} opts @@ -860,30 +807,30 @@ export function create_client({ target, session, base, trailing_slash }) { const params = {}; // error page does not have params const root_layout = await load_node({ - module: await default_layout, + node: await default_layout, url, params, - stuff: {}, - routeId + routeId, + parent: () => Promise.resolve({}), + server_data: null // TODO!!!!! }); - const root_error = await load_node({ - status, - error, - module: await default_error, - url, - params, - stuff: (root_layout && root_layout.loaded && root_layout.loaded.stuff) || {}, - routeId - }); + const root_error = { + node: await default_error, + data: null, + // TODO make this unnecessary + uses: { + params: new Set(), + url: false, + session: false, + dependencies: new Set(), + parent: false + } + }; return await get_navigation_result_from_branch({ url, params, - stuff: { - ...root_layout?.loaded?.stuff, - ...root_error?.loaded?.stuff - }, branch: [root_layout, root_error], status, error, @@ -957,7 +904,6 @@ export function create_client({ target, session, base, trailing_slash }) { await update( url, redirect_chain, - false, { scroll, keepfocus, @@ -1025,7 +971,13 @@ export function create_client({ target, session, base, trailing_slash }) { goto: (href, opts = {}) => goto(href, opts, []), invalidate: (resource) => { - if (typeof resource === 'function') { + if (resource === undefined) { + // Force rerun of all load functions, regardless of their dependencies + for (const node of current.branch) { + node?.uses.dependencies.add(''); + } + invalidated.push(() => true); + } else if (typeof resource === 'function') { invalidated.push(resource); } else { const { href } = new URL(resource, location.href); @@ -1034,7 +986,7 @@ export function create_client({ target, session, base, trailing_slash }) { if (!invalidating) { invalidating = Promise.resolve().then(async () => { - await update(new URL(location.href), [], true); + await update(new URL(location.href), []); invalidating = null; }); @@ -1054,7 +1006,9 @@ export function create_client({ target, session, base, trailing_slash }) { ? routes.filter((route) => pathnames.some((pathname) => route.exec(pathname))) : routes; - const promises = matching.map((r) => Promise.all(r.a.map((load) => load()))); + const promises = matching.map((r) => { + return Promise.all([...r.layouts, r.leaf].map((load) => load?.())); + }); await Promise.all(promises); }, @@ -1250,97 +1204,65 @@ export function create_client({ target, session, base, trailing_slash }) { }); }, - _hydrate: async ({ status, error, nodes, params, routeId }) => { + _hydrate: async ({ status, error, node_ids, params, routeId }) => { const url = new URL(location.href); - /** @type {Array} */ - const branch = []; - - /** @type {Record} */ - let stuff = {}; - - /** @type {import('./types').NavigationResult | undefined} */ + /** @type {import('./types').NavigationFinished | undefined} */ let result; - let error_args; - try { - for (let i = 0; i < nodes.length; i += 1) { - const is_leaf = i === nodes.length - 1; - - let props; + const script = document.querySelector(`script[sveltekit\\:data-type="server_data"]`); + const server_data = script?.textContent ? JSON.parse(script.textContent) : []; - if (is_leaf) { - const serialized = document.querySelector('script[sveltekit\\:data-type="props"]'); - if (serialized) { - props = JSON.parse(/** @type {string} */ (serialized.textContent)); - } - } - - const node = await load_node({ - module: await components[nodes[i]](), + const branch_promises = node_ids.map(async (n, i) => { + return load_node({ + node: await nodes[n](), url, params, - stuff, - status: is_leaf ? status : undefined, - error: is_leaf ? error : undefined, - props, - routeId + routeId, + parent: async () => { + const data = {}; + for (let j = 0; j < i; j += 1) { + Object.assign(data, (await branch_promises[j]).data); + } + return data; + }, + server_data: server_data[i] ?? null }); + }); - if (props) { - node.uses.dependencies.add(url.href); - node.uses.url = true; - } + result = await get_navigation_result_from_branch({ + url, + params, + branch: await Promise.all(branch_promises), + status, + error: /** @type {import('../server/page/types').SerializedHttpError} */ (error) + ?.__is_http_error + ? new HttpError( + /** @type {import('../server/page/types').SerializedHttpError} */ (error).status, + error.message + ) + : error, + routeId + }); + } catch (e) { + const error = normalize_error(e); - branch.push(node); - - if (node && node.loaded) { - if (node.loaded.error) { - if (error) throw node.loaded.error; - error_args = { - status: node.loaded.status ?? 500, - error: node.loaded.error, - url, - routeId - }; - } else if (node.loaded.stuff) { - stuff = { - ...stuff, - ...node.loaded.stuff - }; - } - } + if (error instanceof Redirect) { + // this is a real edge case — `load` would need to return + // a redirect but only in the browser + await native_navigation(new URL(/** @type {Redirect} */ (e).location, location.href)); + return; } - result = error_args - ? await load_root_error_page(error_args) - : await get_navigation_result_from_branch({ - url, - params, - stuff, - branch, - status, - error, - routeId - }); - } catch (e) { - if (error) throw e; - result = await load_root_error_page({ - status: 500, - error: coalesce_to_error(e), + status: error instanceof HttpError ? error.status : 500, + error, url, routeId }); } - if (result.redirect) { - // this is a real edge case — `load` would need to return - // a redirect but only in the browser - await native_navigation(new URL(result.redirect, location.href)); - } - initialize(result); } }; diff --git a/packages/kit/src/runtime/client/fetcher.js b/packages/kit/src/runtime/client/fetcher.js index 1dad6a73fa02..e806f9c0a3a2 100644 --- a/packages/kit/src/runtime/client/fetcher.js +++ b/packages/kit/src/runtime/client/fetcher.js @@ -29,7 +29,7 @@ if (import.meta.env.DEV) { const heuristic = can_inspect_stack_trace ? stack.includes('load_node') : loading; if (heuristic) { console.warn( - `Loading ${url} using \`window.fetch\`. For best results, use the \`fetch\` that is passed to your \`load\` function: https://kit.svelte.dev/docs/loading#input-fetch` + `Loading ${url} using \`window.fetch\`. For best results, use the \`fetch\` that is passed to your \`load\` function: https://kit.svelte.dev/docs/load#input-fetch` ); } diff --git a/packages/kit/src/runtime/client/parse.js b/packages/kit/src/runtime/client/parse.js index 294021924293..6acf39b64d10 100644 --- a/packages/kit/src/runtime/client/parse.js +++ b/packages/kit/src/runtime/client/parse.js @@ -1,27 +1,36 @@ import { exec, parse_route_id } from '../../utils/routing.js'; /** - * @param {import('types').CSRComponentLoader[]} components - * @param {Record} dictionary + * @param {import('types').CSRPageNodeLoader[]} nodes + * @param {Record} dictionary * @param {Record boolean>} matchers * @returns {import('types').CSRRoute[]} */ -export function parse(components, dictionary, matchers) { - const routes = Object.entries(dictionary).map(([id, [a, b, has_shadow]]) => { +export function parse(nodes, dictionary, matchers) { + return Object.entries(dictionary).map(([id, [errors, layouts, leaf, uses_server_data]]) => { const { pattern, names, types } = parse_route_id(id); - return { + const route = { id, /** @param {string} path */ exec: (path) => { const match = pattern.exec(path); if (match) return exec(match, names, types, matchers); }, - a: a.map((n) => components[n]), - b: b.map((n) => components[n]), - has_shadow: !!has_shadow + errors: errors.map((n) => nodes[n]), + layouts: layouts.map((n) => nodes[n]), + leaf: nodes[leaf], + uses_server_data: !!uses_server_data }; - }); - return routes; + // bit of a hack, but ensures that layout/error node lists are the same + // length, without which the wrong data will be applied if the route + // manifest looks like `[[a, b], [c,], d]` + route.errors.length = route.layouts.length = Math.max( + route.errors.length, + route.layouts.length + ); + + return route; + }); } diff --git a/packages/kit/src/runtime/client/start.js b/packages/kit/src/runtime/client/start.js index fcba7c748bd6..995a7eeb885d 100644 --- a/packages/kit/src/runtime/client/start.js +++ b/packages/kit/src/runtime/client/start.js @@ -17,8 +17,8 @@ export { set_public_env } from '../env-public.js'; * trailing_slash: import('types').TrailingSlash; * hydrate: { * status: number; - * error: Error; - * nodes: number[]; + * error: Error | (import('../server/page/types').SerializedHttpError); + * node_ids: number[]; * params: Record; * routeId: string | null; * }; diff --git a/packages/kit/src/runtime/client/types.d.ts b/packages/kit/src/runtime/client/types.d.ts index ee5565061dac..1f07a90fcdf5 100644 --- a/packages/kit/src/runtime/client/types.d.ts +++ b/packages/kit/src/runtime/client/types.d.ts @@ -6,7 +6,9 @@ import { prefetch, prefetchRoutes } from '$app/navigation'; -import { CSRComponent, CSRRoute, NormalizedLoadOutput } from 'types'; +import { CSRPageNode, CSRRoute, JSONObject } from 'types'; +import { HttpError } from '../../index/private.js'; +import { SerializedHttpError } from '../server/page/types.js'; export interface Client { // public API, exposed via $app/navigation @@ -21,8 +23,8 @@ export interface Client { // private API _hydrate: (opts: { status: number; - error: Error; - nodes: number[]; + error: Error | SerializedHttpError; + node_ids: number[]; params: Record; routeId: string | null; }) => Promise; @@ -48,30 +50,57 @@ export type NavigationIntent = { url: URL; }; -export type NavigationResult = { - redirect?: string; +export type NavigationResult = NavigationRedirect | NavigationFinished; + +export type NavigationRedirect = { + type: 'redirect'; + location: string; +}; + +export type NavigationFinished = { + type: 'loaded'; state: NavigationState; props: Record; }; export type BranchNode = { - module: CSRComponent; - loaded: NormalizedLoadOutput | null; + node: CSRPageNode; + data: Record | null; uses: { params: Set; url: boolean; // TODO make more granular? session: boolean; - stuff: boolean; dependencies: Set; + parent: boolean; }; - stuff: Record; }; export type NavigationState = { branch: Array; - error: Error | null; + error: HttpError | Error | null; params: Record; session_id: number; - stuff: Record; url: URL; }; + +export type ServerDataPayload = ServerDataRedirected | ServerDataLoaded; + +export interface ServerDataRedirected { + type: 'redirect'; + location: string; +} + +export interface ServerDataLoaded { + type: 'data'; + nodes: Array<{ + data?: JSONObject | null; // TODO or `-1` to indicate 'reuse cached data'? + status?: number; + message?: string; + error?: { + name: string; + message: string; + stack: string; + [key: string]: any; + }; + }>; +} diff --git a/packages/kit/src/runtime/components/error.svelte b/packages/kit/src/runtime/components/error.svelte index 4c3541911212..f1d30a89bb79 100644 --- a/packages/kit/src/runtime/components/error.svelte +++ b/packages/kit/src/runtime/components/error.svelte @@ -1,29 +1,16 @@ - - -

{status}

+

{$page.status}

-
{error.message}
+
{$page.error.message}
-{#if error.frame} -
{error.frame}
+{#if $page.error.frame} +
{$page.error.frame}
{/if} -{#if error.stack} -
{error.stack}
+{#if $page.error.stack} +
{$page.error.stack}
{/if} diff --git a/packages/kit/src/runtime/load.js b/packages/kit/src/runtime/load.js deleted file mode 100644 index 974ba0ff104a..000000000000 --- a/packages/kit/src/runtime/load.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * @param {import('types').LoadOutput | void} loaded - * @returns {import('types').NormalizedLoadOutput} - */ -export function normalize(loaded) { - if (!loaded) { - return {}; - } - - // TODO remove for 1.0 - // @ts-expect-error - if (loaded.fallthrough) { - throw new Error( - 'fallthrough is no longer supported. Use matchers instead: https://kit.svelte.dev/docs/routing#advanced-routing-matching' - ); - } - - // TODO remove for 1.0 - if ('maxage' in loaded) { - throw new Error('maxage should be replaced with cache: { maxage }'); - } - - const has_error_status = - loaded.status && loaded.status >= 400 && loaded.status <= 599 && !loaded.redirect; - if (loaded.error || has_error_status) { - const status = loaded.status; - - if (!loaded.error && has_error_status) { - return { - status: status || 500, - error: new Error(`${status}`) - }; - } - - const error = typeof loaded.error === 'string' ? new Error(loaded.error) : loaded.error; - - if (!(error instanceof Error)) { - return { - status: 500, - error: new Error( - `"error" property returned from load() must be a string or instance of Error, received type "${typeof error}"` - ) - }; - } - - if (!status || status < 400 || status > 599) { - console.warn('"error" returned from load() without a valid status code — defaulting to 500'); - return { status: 500, error }; - } - - return { status, error }; - } - - if (loaded.redirect) { - if (!loaded.status || Math.floor(loaded.status / 100) !== 3) { - throw new Error( - '"redirect" property returned from load() must be accompanied by a 3xx status code' - ); - } - - if (typeof loaded.redirect !== 'string') { - throw new Error('"redirect" property returned from load() must be a string'); - } - } - - if (loaded.dependencies) { - if ( - !Array.isArray(loaded.dependencies) || - loaded.dependencies.some((dep) => typeof dep !== 'string') - ) { - throw new Error('"dependencies" property returned from load() must be of type string[]'); - } - } - - // TODO remove before 1.0 - if (/** @type {any} */ (loaded).context) { - throw new Error( - 'You are returning "context" from a load function. ' + - '"context" was renamed to "stuff", please adjust your code accordingly.' - ); - } - - return /** @type {import('types').NormalizedLoadOutput} */ (loaded); -} diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js index 89ef59c297e6..ab6cbfc027e6 100644 --- a/packages/kit/src/runtime/server/endpoint.js +++ b/packages/kit/src/runtime/server/endpoint.js @@ -1,53 +1,18 @@ -import { to_headers } from '../../utils/http.js'; -import { hash } from '../hash.js'; -import { check_method_names, is_pojo, serialize_error } from './utils.js'; - -/** @param {string} body */ -function error(body) { - return new Response(body, { - status: 500 - }); -} - -/** @param {unknown} s */ -function is_string(s) { - return typeof s === 'string' || s instanceof String; -} - -const text_types = new Set([ - 'application/xml', - 'application/json', - 'application/x-www-form-urlencoded', - 'multipart/form-data' -]); - -const bodyless_status_codes = new Set([101, 204, 205, 304]); - -/** - * Decides how the body should be parsed based on its mime type - * - * @param {string | undefined | null} content_type The `content-type` header of a request/response. - * @returns {boolean} - */ -export function is_text(content_type) { - if (!content_type) return true; // defaults to json - const type = content_type.split(';')[0].toLowerCase(); // get the mime type - - return type.startsWith('text/') || type.endsWith('+xml') || text_types.has(type); -} +import { check_method_names, method_not_allowed } from './utils.js'; /** * @param {import('types').RequestEvent} event - * @param {{ [method: string]: import('types').RequestHandler }} mod - * @param {import('types').SSROptions} options + * @param {import('types').SSREndpoint} route * @returns {Promise} */ -export async function render_endpoint(event, mod, options) { - const { method } = event.request; +export async function render_endpoint(event, route) { + const method = /** @type {import('types').HttpMethod} */ (event.request.method); + const mod = await route.load(); + + // TODO: Remove for 1.0 check_method_names(mod); - /** @type {import('types').RequestHandler} */ let handler = mod[method]; if (!handler && method === 'HEAD') { @@ -55,88 +20,23 @@ export async function render_endpoint(event, mod, options) { } if (!handler) { - const allowed = []; - - for (const method in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) { - if (mod[method]) allowed.push(method); + if (event.request.headers.get('x-sveltekit-load')) { + // TODO would be nice to avoid these requests altogether, + // by noting whether or not page endpoints export `get` + return new Response(undefined, { status: 204 }); } - if (mod.GET || mod.HEAD) allowed.push('HEAD'); - - return event.request.headers.get('x-sveltekit-load') - ? // TODO would be nice to avoid these requests altogether, - // by noting whether or not page endpoints export `get` - new Response(undefined, { - status: 204 - }) - : new Response(`${method} method not allowed`, { - status: 405, - headers: { - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 - // "The server must generate an Allow header field in a 405 status code response" - allow: allowed.join(', ') - } - }); + return method_not_allowed(mod, method); } const response = await handler(event); - const preface = `Invalid response from route ${event.url.pathname}`; - if (typeof response !== 'object') { - return error(`${preface}: expected an object, got ${typeof response}`); - } - - // TODO remove for 1.0 - // @ts-expect-error - if (response.fallthrough) { - throw new Error( - 'fallthrough is no longer supported. Use matchers instead: https://kit.svelte.dev/docs/routing#advanced-routing-matching' - ); - } - - const { status = 200, body = {} } = response; - const headers = - response.headers instanceof Headers - ? new Headers(response.headers) - : to_headers(response.headers); - - const type = headers.get('content-type'); - - if ( - !is_text(type) && - !(body instanceof Uint8Array || body instanceof ReadableStream || is_string(body)) - ) { - return error( - `${preface}: body must be an instance of string, Uint8Array or ReadableStream if content-type is not a supported textual content-type` + if (!(response instanceof Response)) { + return new Response( + `Invalid response from route ${event.url.pathname}: handler should return a Response object`, + { status: 500 } ); } - /** @type {import('types').StrictBody} */ - let normalized_body; - - if (is_pojo(body) && (!type || type.startsWith('application/json'))) { - headers.set('content-type', 'application/json; charset=utf-8'); - normalized_body = - body instanceof Error ? serialize_error(body, options.get_stack) : JSON.stringify(body); - } else { - normalized_body = /** @type {import('types').StrictBody} */ (body); - } - - if ( - (typeof normalized_body === 'string' || normalized_body instanceof Uint8Array) && - !headers.has('etag') - ) { - const cache_control = headers.get('cache-control'); - if (!cache_control || !/(no-store|immutable)/.test(cache_control)) { - headers.set('etag', `"${hash(normalized_body)}"`); - } - } - - return new Response( - method !== 'HEAD' && !bodyless_status_codes.has(status) ? normalized_body : undefined, - { - status, - headers - } - ); + return response; } diff --git a/packages/kit/src/runtime/server/endpoint.spec.js b/packages/kit/src/runtime/server/endpoint.spec.js deleted file mode 100644 index 52a9a7e491b9..000000000000 --- a/packages/kit/src/runtime/server/endpoint.spec.js +++ /dev/null @@ -1,24 +0,0 @@ -import { test } from 'uvu'; -import * as assert from 'uvu/assert'; -import { is_text } from './endpoint.js'; - -test('is_text', () => { - assert.equal(is_text(undefined), true); - assert.equal(is_text(null), true); - assert.equal(is_text(''), true); - assert.equal(is_text('TEXT/PLAIN'), true); - assert.equal(is_text('text/html'), true); - assert.equal(is_text('text/javascript'), true); - assert.equal(is_text('application/xml'), true); - assert.equal(is_text('image/svg+xml'), true); - assert.equal(is_text('application/json'), true); - assert.equal(is_text('text/plain; charset="us-ascii"'), true); - assert.equal(is_text('multipart/form-data; boundary=aBoundaryString'), true); - - assert.equal(is_text('multipart/byteranges; boundary=3d6b6a416f9b5'), false); - assert.equal(is_text('image/apng'), false); - assert.equal(is_text('IMAGE/webp'), false); - assert.equal(is_text('application/octet-stream'), false); -}); - -test.run(); diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 04406081ea58..c35a45c6a05b 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -2,11 +2,14 @@ import { render_endpoint } from './endpoint.js'; import { render_page } from './page/index.js'; import { render_response } from './page/render.js'; import { respond_with_error } from './page/respond_with_error.js'; -import { coalesce_to_error } from '../../utils/error.js'; -import { serialize_error, GENERIC_ERROR } from './utils.js'; +import { coalesce_to_error, normalize_error } from '../../utils/error.js'; +import { serialize_error, GENERIC_ERROR, error_to_pojo } from './utils.js'; import { decode_params, normalize_path } from '../../utils/url.js'; import { exec } from '../../utils/routing.js'; import { negotiate } from '../../utils/http.js'; +import { HttpError, Redirect } from '../../index/private.js'; +import { load_server_data } from './page/load_data.js'; +import { json } from '../../index/index.js'; /* global __SVELTEKIT_ADAPTER_NAME__ */ @@ -112,6 +115,9 @@ export async function respond(request, options, state) { } } + /** @type {import('types').ResponseHeaders} */ + const headers = {}; + /** @type {import('types').RequestEvent} */ const event = { get clientAddress() { @@ -132,6 +138,22 @@ export async function respond(request, options, state) { platform: state.platform, request, routeId: route && route.id, + setHeaders: (new_headers) => { + for (const key in new_headers) { + const lower = key.toLowerCase(); + + if (lower in headers) { + throw new Error(`"${key}" header is already set`); + } + + // TODO apply these headers to the response + headers[lower] = new_headers[key]; + + if (state.prerendering && lower === 'cache-control') { + state.prerendering.cache = /** @type {string} */ (new_headers[key]); + } + } + }, url }; @@ -202,10 +224,12 @@ export async function respond(request, options, state) { state, $session: await options.hooks.getSession(event), page_config: { router: true, hydrate: true }, - stuff: {}, status: 200, error: null, branch: [], + fetched: [], + validation_errors: undefined, + cookies: [], resolve_opts: { ...resolve_opts, ssr: false @@ -216,70 +240,115 @@ export async function respond(request, options, state) { if (route) { /** @type {Response} */ let response; - - if (is_data_request && route.type === 'page' && route.shadow) { - response = await render_endpoint(event, await route.shadow(), options); - - // loading data for a client-side transition is a special case - if (request.headers.has('x-sveltekit-load')) { - // since redirects are opaque to the browser, we need to repackage - // 3xx responses as 200s with a custom header - if (response.status >= 300 && response.status < 400) { - const location = response.headers.get('location'); - - if (location) { - const headers = new Headers(response.headers); - headers.set('x-sveltekit-location', location); - response = new Response(undefined, { - status: 204, - headers - }); + if (is_data_request && route.type === 'page') { + try { + /** @type {Redirect | HttpError | Error} */ + let error; + + // TODO only get the data we need for the navigation + const promises = [...route.layouts, route.leaf].map(async (n, i) => { + try { + if (error) return; + + const node = n ? await options.manifest._.nodes[n]() : undefined; + return { + // TODO return `uses`, so we can reuse server data effectively + data: await load_server_data({ + event, + node, + parent: async () => { + /** @type {import('types').JSONObject} */ + const data = {}; + for (let j = 0; j < i; j += 1) { + Object.assign(data, await promises[j]); + } + return data; + } + }) + }; + } catch (e) { + error = normalize_error(e); + + if (error instanceof Redirect) { + throw error; + } + + if (error instanceof HttpError) { + return error; // { status, message } + } + + options.handle_error(error, event); + + return { + error: error_to_pojo(error, options.get_stack) + }; } + }); + + response = json({ + type: 'data', + nodes: await Promise.all(promises) + }); + } catch (e) { + const error = normalize_error(e); + + if (error instanceof Redirect) { + response = json({ + type: 'redirect', + location: error.location + }); + } else { + response = json(error_to_pojo(error, options.get_stack), { status: 500 }); } } } else { response = route.type === 'endpoint' - ? await render_endpoint(event, await route.load(), options) + ? await render_endpoint(event, route) : await render_page(event, route, options, state, resolve_opts); } - if (response) { - // respond with 304 if etag matches - if (response.status === 200 && response.headers.has('etag')) { - let if_none_match_value = request.headers.get('if-none-match'); - - // ignore W/ prefix https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match#directives - if (if_none_match_value?.startsWith('W/"')) { - if_none_match_value = if_none_match_value.substring(2); + for (const key in headers) { + const value = headers[key]; + if (key === 'set-cookie') { + for (const cookie of Array.isArray(value) ? value : [value]) { + response.headers.append(key, /** @type {string} */ (cookie)); } + } else if (!is_data_request) { + // we only want to set cookies on __data.json requests, we don't + // want to cache stuff erroneously etc + response.headers.set(key, /** @type {string} */ (value)); + } + } - const etag = /** @type {string} */ (response.headers.get('etag')); - - if (if_none_match_value === etag) { - const headers = new Headers({ etag }); - - // https://datatracker.ietf.org/doc/html/rfc7232#section-4.1 - for (const key of [ - 'cache-control', - 'content-location', - 'date', - 'expires', - 'vary' - ]) { - const value = response.headers.get(key); - if (value) headers.set(key, value); - } + // respond with 304 if etag matches + if (response.status === 200 && response.headers.has('etag')) { + let if_none_match_value = request.headers.get('if-none-match'); - return new Response(undefined, { - status: 304, - headers - }); - } + // ignore W/ prefix https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match#directives + if (if_none_match_value?.startsWith('W/"')) { + if_none_match_value = if_none_match_value.substring(2); } - return response; + const etag = /** @type {string} */ (response.headers.get('etag')); + + if (if_none_match_value === etag) { + const headers = new Headers({ etag }); + + // https://datatracker.ietf.org/doc/html/rfc7232#section-4.1 + for (const key of ['cache-control', 'content-location', 'date', 'expires', 'vary']) { + const value = response.headers.get(key); + if (value) headers.set(key, value); + } + + return new Response(undefined, { + status: 304, + headers + }); + } } + + return response; } if (state.initiator === GENERIC_ERROR) { diff --git a/packages/kit/src/runtime/server/page/fetch.js b/packages/kit/src/runtime/server/page/fetch.js new file mode 100644 index 000000000000..6790844b70af --- /dev/null +++ b/packages/kit/src/runtime/server/page/fetch.js @@ -0,0 +1,265 @@ +import * as cookie from 'cookie'; +import * as set_cookie_parser from 'set-cookie-parser'; +import { respond } from '../index.js'; +import { is_root_relative, resolve } from '../../../utils/url.js'; +import { domain_matches, path_matches } from './cookie.js'; + +/** + * @param {{ + * event: import('types').RequestEvent; + * options: import('types').SSROptions; + * state: import('types').SSRState; + * route: import('types').SSRPage | import('types').SSRErrorPage; + * }} opts + */ +export function create_fetch({ event, options, state, route }) { + /** @type {import('./types').Fetched[]} */ + const fetched = []; + + const initial_cookies = cookie.parse(event.request.headers.get('cookie') || ''); + + /** @type {import('set-cookie-parser').Cookie[]} */ + const cookies = []; + + /** @type {typeof fetch} */ + const fetcher = async (resource, opts = {}) => { + /** @type {string} */ + let requested; + + if (typeof resource === 'string' || resource instanceof URL) { + requested = resource.toString(); + } else { + requested = resource.url; + + opts = { + method: resource.method, + headers: resource.headers, + body: resource.body, + mode: resource.mode, + credentials: resource.credentials, + cache: resource.cache, + redirect: resource.redirect, + referrer: resource.referrer, + integrity: resource.integrity, + ...opts + }; + } + + opts.headers = new Headers(opts.headers); + + // merge headers from request + for (const [key, value] of event.request.headers) { + if ( + key !== 'authorization' && + key !== 'connection' && + key !== 'cookie' && + key !== 'host' && + key !== 'if-none-match' && + !opts.headers.has(key) + ) { + opts.headers.set(key, value); + } + } + + const resolved = resolve(event.url.pathname, requested.split('?')[0]); + + /** @type {Response} */ + let response; + + /** @type {import('types').PrerenderDependency} */ + let dependency; + + // handle fetch requests for static assets. e.g. prebaked data, etc. + // we need to support everything the browser's fetch supports + const prefix = options.paths.assets || options.paths.base; + const filename = decodeURIComponent( + resolved.startsWith(prefix) ? resolved.slice(prefix.length) : resolved + ).slice(1); + const filename_html = `${filename}/index.html`; // path may also match path/index.html + + const is_asset = options.manifest.assets.has(filename); + const is_asset_html = options.manifest.assets.has(filename_html); + + if (is_asset || is_asset_html) { + const file = is_asset ? filename : filename_html; + + if (options.read) { + const type = is_asset + ? options.manifest.mimeTypes[filename.slice(filename.lastIndexOf('.'))] + : 'text/html'; + + response = new Response(options.read(file), { + headers: type ? { 'content-type': type } : {} + }); + } else { + response = await fetch(`${event.url.origin}/${file}`, /** @type {RequestInit} */ (opts)); + } + } else if (is_root_relative(resolved)) { + if (opts.credentials !== 'omit') { + const authorization = event.request.headers.get('authorization'); + + // combine cookies from the initiating request with any that were + // added via set-cookie + const combined_cookies = { ...initial_cookies }; + + for (const cookie of cookies) { + if (!domain_matches(event.url.hostname, cookie.domain)) continue; + if (!path_matches(resolved, cookie.path)) continue; + + combined_cookies[cookie.name] = cookie.value; + } + + const cookie = Object.entries(combined_cookies) + .map(([name, value]) => `${name}=${value}`) + .join('; '); + + if (cookie) { + opts.headers.set('cookie', cookie); + } + + if (authorization && !opts.headers.has('authorization')) { + opts.headers.set('authorization', authorization); + } + } + + if (opts.body && typeof opts.body !== 'string') { + // per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a + // Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object. + // non-string bodies are irksome to deal with, but luckily aren't particularly useful + // in this context anyway, so we take the easy route and ban them + throw new Error('Request body must be a string'); + } + + response = await respond( + new Request(new URL(requested, event.url).href, { ...opts }), + options, + { + ...state, + initiator: route + } + ); + + if (state.prerendering) { + dependency = { response, body: null }; + state.prerendering.dependencies.set(resolved, dependency); + } + } else { + // external + if (resolved.startsWith('//')) { + requested = event.url.protocol + requested; + } + + // external fetch + // allow cookie passthrough for "same-origin" + // if SvelteKit is serving my.domain.com: + // - domain.com WILL NOT receive cookies + // - my.domain.com WILL receive cookies + // - api.domain.dom WILL NOT receive cookies + // - sub.my.domain.com WILL receive cookies + // ports do not affect the resolution + // leading dot prevents mydomain.com matching domain.com + if ( + `.${new URL(requested).hostname}`.endsWith(`.${event.url.hostname}`) && + opts.credentials !== 'omit' + ) { + const cookie = event.request.headers.get('cookie'); + if (cookie) opts.headers.set('cookie', cookie); + } + + // we need to delete the connection header, as explained here: + // https://github.com/nodejs/undici/issues/1470#issuecomment-1140798467 + // TODO this may be a case for being selective about which headers we let through + opts.headers.delete('connection'); + + const external_request = new Request(requested, /** @type {RequestInit} */ (opts)); + response = await options.hooks.externalFetch.call(null, external_request); + } + + const set_cookie = response.headers.get('set-cookie'); + if (set_cookie) { + cookies.push( + ...set_cookie_parser + .splitCookiesString(set_cookie) + .map((str) => set_cookie_parser.parseString(str)) + ); + } + + const proxy = new Proxy(response, { + get(response, key, _receiver) { + async function text() { + const body = await response.text(); + + /** @type {import('types').ResponseHeaders} */ + const headers = {}; + for (const [key, value] of response.headers) { + // TODO skip others besides set-cookie and etag? + if (key !== 'set-cookie' && key !== 'etag') { + headers[key] = value; + } + } + + if (!opts.body || typeof opts.body === 'string') { + const status_number = Number(response.status); + if (isNaN(status_number)) { + throw new Error( + `response.status is not a number. value: "${ + response.status + }" type: ${typeof response.status}` + ); + } + + fetched.push({ + url: requested, + body: opts.body, + response: { + status: status_number, + statusText: response.statusText, + headers, + body + } + }); + } + + if (dependency) { + dependency.body = body; + } + + return body; + } + + if (key === 'arrayBuffer') { + return async () => { + const buffer = await response.arrayBuffer(); + + if (dependency) { + dependency.body = new Uint8Array(buffer); + } + + // TODO should buffer be inlined into the page (albeit base64'd)? + // any conditions in which it shouldn't be? + + return buffer; + }; + } + + if (key === 'text') { + return text; + } + + if (key === 'json') { + return async () => { + return JSON.parse(await text()); + }; + } + + // TODO arrayBuffer? + + return Reflect.get(response, key, response); + } + }); + + return proxy; + }; + + return { fetcher, fetched, cookies }; +} diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index c56e97b05270..fc84e687d079 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -1,6 +1,19 @@ import { negotiate } from '../../../utils/http.js'; -import { render_endpoint } from '../endpoint.js'; -import { respond } from './respond.js'; +import { render_response } from './render.js'; +import { respond_with_error } from './respond_with_error.js'; +import { method_not_allowed, error_to_pojo, allowed_methods } from '../utils.js'; +import { create_fetch } from './fetch.js'; +import { HttpError, Redirect } from '../../../index/private.js'; +import { error, json } from '../../../index/index.js'; +import { normalize_error } from '../../../utils/error.js'; +import { load_data, load_server_data } from './load_data.js'; + +/** + * @typedef {import('./types.js').Loaded} Loaded + * @typedef {import('types').SSRNode} SSRNode + * @typedef {import('types').SSROptions} SSROptions + * @typedef {import('types').SSRState} SSRState + */ /** * @param {import('types').RequestEvent} event @@ -18,25 +31,393 @@ export async function render_page(event, route, options, state, resolve_opts) { }); } - if (route.shadow) { - const type = negotiate(event.request.headers.get('accept') || 'text/html', [ - 'text/html', - 'application/json' - ]); + const accept = negotiate(event.request.headers.get('accept') || 'text/html', [ + 'text/html', + 'application/json' + ]); - if (type === 'application/json') { - return render_endpoint(event, await route.shadow(), options); + if (accept === 'application/json') { + const node = await options.manifest._.nodes[route.leaf](); + if (node.server) { + return handle_json_request(event, options, node.server); } } const $session = await options.hooks.getSession(event); - return respond({ - event, - options, - state, - $session, - resolve_opts, - route + const { fetcher, fetched, cookies } = create_fetch({ event, options, state, route }); + + try { + const nodes = await Promise.all([ + // we use == here rather than === because [undefined] serializes as "[null]" + ...route.layouts.map((n) => (n == undefined ? n : options.manifest._.nodes[n]())), + options.manifest._.nodes[route.leaf]() + ]); + + const leaf_node = /** @type {import('types').SSRNode} */ (nodes.at(-1)); + + let status = 200; + + /** @type {HttpError | Error} */ + let mutation_error; + + /** @type {Record | undefined} */ + let validation_errors; + + if (leaf_node.server && event.request.method !== 'GET' && event.request.method !== 'HEAD') { + // for non-GET requests, first call handler in +page.server.js + // (this also determines status code) + try { + const method = /** @type {'POST' | 'PATCH' | 'PUT' | 'DELETE'} */ (event.request.method); + const handler = leaf_node.server[method]; + if (handler) { + const result = await handler.call(null, event); + + if (result?.errors) { + validation_errors = result.errors; + status = result.status ?? 400; + } + + if (event.request.method === 'POST' && result?.location) { + return redirect_response(status, result.location); + } + } else { + event.setHeaders({ + allow: allowed_methods(leaf_node.server).join(', ') + }); + + mutation_error = error(405, 'Method not allowed'); + } + } catch (e) { + if (e instanceof Redirect) { + return redirect_response(e.status, e.location); + } + + mutation_error = /** @type {HttpError | Error} */ (e); + } + } + + if (!resolve_opts.ssr) { + return await render_response({ + branch: [], + validation_errors: undefined, + fetched, + cookies, + page_config: { + hydrate: true, + router: true + }, + status, + error: null, + event, + options, + state, + $session, + resolve_opts + }); + } + + const should_prerender = leaf_node.shared?.prerender ?? options.prerender.default; + if (should_prerender) { + const mod = leaf_node.server; + if (mod && (mod.POST || mod.PUT || mod.DELETE || mod.PATCH)) { + throw new Error('Cannot prerender pages that have endpoints with mutative methods'); + } + } else if (state.prerendering) { + // if the page isn't marked as prerenderable (or is explicitly + // marked NOT prerenderable, if `prerender.default` is `true`), + // then bail out at this point + if (!should_prerender) { + return new Response(undefined, { + status: 204 + }); + } + } + + /** @type {Array} */ + let branch = []; + + /** @type {Error | null} */ + let load_error = null; + + /** @type {Array>} */ + const server_promises = nodes.map((node, i) => { + if (load_error) { + // if an error happens immediately, don't bother with the rest of the nodes + throw load_error; + } + + return Promise.resolve().then(async () => { + try { + if (node === leaf_node && mutation_error) { + // we wait until here to throw the error so that we can use + // any nested +error.svelte components that were defined + throw mutation_error; + } + + return await load_server_data({ + event, + node, + parent: async () => { + /** @type {import('types').JSONObject} */ + const data = {}; + for (let j = 0; j < i; j += 1) { + Object.assign(data, await server_promises[j]); + } + return data; + } + }); + } catch (e) { + load_error = /** @type {Error} */ (e); + throw load_error; + } + }); + }); + + /** @type {Array | null>>} */ + const load_promises = nodes.map((node, i) => { + if (load_error) throw load_error; + return Promise.resolve().then(async () => { + try { + return await load_data({ + $session, + event, + fetcher, + node, + options, + parent: async () => { + const data = {}; + for (let j = 0; j < i; j += 1) { + Object.assign(data, await load_promises[j]); + } + return data; + }, + server_data_promise: server_promises[i], + state + }); + } catch (e) { + load_error = /** @type {Error} */ (e); + throw load_error; + } + }); + }); + + // if we don't do this, rejections will be unhandled + for (const p of server_promises) p.catch(() => {}); + for (const p of load_promises) p.catch(() => {}); + + for (let i = 0; i < nodes.length; i += 1) { + const node = nodes[i]; + + if (node) { + try { + const server_data = await server_promises[i]; + const data = await load_promises[i]; + + branch.push({ node, server_data, data }); + } catch (e) { + const error = normalize_error(e); + + if (error instanceof Redirect) { + return redirect_response(error.status, error.location); + } + + if (!(error instanceof HttpError)) { + options.handle_error(/** @type {Error} */ (error), event); + } + + const status = error instanceof HttpError ? error.status : 500; + + while (i--) { + if (route.errors[i]) { + const index = /** @type {number} */ (route.errors[i]); + const node = await options.manifest._.nodes[index](); + + let j = i; + while (!branch[j]) j -= 1; + + return await render_response({ + event, + options, + state, + $session, + resolve_opts, + page_config: { router: true, hydrate: true }, + status, + error, + branch: compact(branch.slice(0, j + 1)).concat({ + node, + data: null, + server_data: null + }), + fetched, + cookies, + validation_errors: undefined + }); + } + } + + // if we're still here, it means the error happened in the root layout, + // which means we have to fall back to a plain text response + // TODO since the requester is expecting HTML, maybe it makes sense to + // doll this up a bit + return new Response( + error instanceof HttpError ? error.message : options.get_stack(error), + { status } + ); + } + } else { + // push an empty slot so we can rewind past gaps to the + // layout that corresponds with an +error.svelte page + branch.push(null); + } + } + + // generate __data.json files when prerendering + if (state.prerendering && nodes.some((node) => node?.server)) { + const pathname = `${event.url.pathname.replace(/\/$/, '')}/__data.json`; + + const dependency = { + response: new Response(undefined), + body: JSON.stringify({ + type: 'data', + nodes: branch.map((branch_node) => ({ data: branch_node?.server_data })) + }) + }; + + state.prerendering.dependencies.set(pathname, dependency); + } + + // TODO use validation_errors + + return await render_response({ + event, + options, + state, + $session, + resolve_opts, + page_config: get_page_config(leaf_node, options), + status, + error: null, + branch: compact(branch), + validation_errors, + fetched, + cookies + }); + } catch (error) { + // if we end up here, it means the data loaded successfull + // but the page failed to render + options.handle_error(/** @type {Error} */ (error), event); + + return await respond_with_error({ + event, + options, + state, + $session, + status: 500, + error: /** @type {Error} */ (error), + resolve_opts + }); + } +} + +/** + * @param {import('types').SSRNode} leaf + * @param {SSROptions} options + */ +function get_page_config(leaf, options) { + // TODO we can reinstate this now that it's in the module + if (leaf.shared && 'ssr' in leaf.shared) { + throw new Error( + '`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs/hooks#handle' + ); + } + + return { + router: leaf.shared?.router ?? options.router, + hydrate: leaf.shared?.hydrate ?? options.hydrate + }; +} + +/** + * @param {import('types').RequestEvent} event + * @param {import('types').SSROptions} options + * @param {import('types').SSRNode['server']} mod + */ +export async function handle_json_request(event, options, mod) { + const method = /** @type {import('types').HttpMethod} */ (event.request.method); + const handler = mod[method === 'HEAD' || method === 'GET' ? 'load' : method]; + + if (!handler) { + return method_not_allowed(mod, method); + } + + try { + // @ts-ignore + const result = await handler.call(null, event); + + if (method === 'HEAD') { + return new Response(); + } + + if (method === 'GET') { + return json(result); + } + + if (method === 'POST') { + // @ts-ignore + if (result.errors) { + // @ts-ignore + return json({ errors: result.errors }, { status: result.status || 400 }); + } + + return new Response(undefined, { + status: 201, + // @ts-ignore + headers: result.location ? { location: result.location } : undefined + }); + } + + return new Response(undefined, { status: 204 }); + } catch (e) { + const error = normalize_error(e); + + if (error instanceof Redirect) { + return redirect_response(error.status, error.location); + } + + if (!(error instanceof HttpError)) { + options.handle_error(error, event); + } + + return json(error_to_pojo(error, options.get_stack), { + status: error instanceof HttpError ? error.status : 500 + }); + } +} + +/** + * @param {number} status + * @param {string} location + */ +function redirect_response(status, location) { + return new Response(undefined, { + status, + headers: { location } }); } + +/** + * @template T + * @param {Array} array + * @returns {T[]} + */ +function compact(array) { + const compacted = []; + for (const item of array) { + if (item) { + compacted.push(item); + } + } + return compacted; +} diff --git a/packages/kit/src/runtime/server/page/load_data.js b/packages/kit/src/runtime/server/page/load_data.js new file mode 100644 index 000000000000..08c308a92724 --- /dev/null +++ b/packages/kit/src/runtime/server/page/load_data.js @@ -0,0 +1,94 @@ +import { LoadURL, PrerenderingURL } from '../../../utils/url.js'; + +/** + * Calls the user's `load` function. + * @param {{ + * event: import('types').RequestEvent; + * node: import('types').SSRNode | undefined; + * parent: () => Promise; + * }} opts + */ +export async function load_server_data({ event, node, parent }) { + if (!node?.server) return null; + + const server_data = await node.server.load?.call(null, { + // can't use destructuring here because it will always + // invoke event.clientAddress, which breaks prerendering + get clientAddress() { + return event.clientAddress; + }, + locals: event.locals, + params: event.params, + parent, + platform: event.platform, + request: event.request, + routeId: event.routeId, + setHeaders: event.setHeaders, + url: event.url + }); + + return server_data ? unwrap_promises(server_data) : null; +} + +/** + * Calls the user's `load` function. + * @param {{ + * $session: Record; + * event: import('types').RequestEvent; + * fetcher: typeof fetch; + * node: import('types').SSRNode | undefined; + * options: import('types').SSROptions; + * parent: () => Promise>; + * server_data_promise: Promise; + * state: import('types').SSRState; + * }} opts + */ +export async function load_data({ + $session, + event, + fetcher, + node, + options, + parent, + server_data_promise, + state +}) { + const server_data = await server_data_promise; + + if (!node?.shared?.load) { + return server_data; + } + + const data = await node.shared.load.call(null, { + url: state.prerendering ? new PrerenderingURL(event.url) : new LoadURL(event.url), + params: event.params, + data: server_data, + routeId: event.routeId, + get session() { + if (node.shared.prerender ?? options.prerender.default) { + throw Error( + 'Attempted to access session from a prerendered page. Session would never be populated.' + ); + } + return $session; + }, + fetch: fetcher, + setHeaders: event.setHeaders, + depends: () => {}, + parent + }); + + return data ? unwrap_promises(data) : null; +} + +/** @param {Record} object */ +async function unwrap_promises(object) { + /** @type {import('types').JSONObject} */ + const unwrapped = {}; + + for (const key in object) { + unwrapped[key] = await object[key]; + } + + return unwrapped; +} diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js deleted file mode 100644 index fbf789222522..000000000000 --- a/packages/kit/src/runtime/server/page/load_node.js +++ /dev/null @@ -1,560 +0,0 @@ -import * as cookie from 'cookie'; -import * as set_cookie_parser from 'set-cookie-parser'; -import { normalize } from '../../load.js'; -import { respond } from '../index.js'; -import { LoadURL, PrerenderingURL, is_root_relative, resolve } from '../../../utils/url.js'; -import { check_method_names, is_pojo, lowercase_keys } from '../utils.js'; -import { coalesce_to_error } from '../../../utils/error.js'; -import { domain_matches, path_matches } from './cookie.js'; - -/** - * Calls the user's `load` function. - * @param {{ - * event: import('types').RequestEvent; - * options: import('types').SSROptions; - * state: import('types').SSRState; - * route: import('types').SSRPage | import('types').SSRErrorPage; - * node: import('types').SSRNode; - * $session: any; - * stuff: Record; - * is_error: boolean; - * is_leaf: boolean; - * status?: number; - * error?: Error; - * }} opts - * @returns {Promise} - */ -export async function load_node({ - event, - options, - state, - route, - node, - $session, - stuff, - is_error, - is_leaf, - status, - error -}) { - const { module } = node; - - let uses_credentials = false; - - /** @type {Array} */ - const fetched = []; - - const cookies = cookie.parse(event.request.headers.get('cookie') || ''); - - /** @type {import('set-cookie-parser').Cookie[]} */ - const new_cookies = []; - - /** @type {import('types').NormalizedLoadOutput} */ - let loaded; - - const should_prerender = node.module.prerender ?? options.prerender.default; - - /** @type {import('types').ShadowData} */ - const shadow = is_leaf - ? await load_shadow_data( - /** @type {import('types').SSRPage} */ (route), - event, - options, - should_prerender - ) - : {}; - - if (shadow.cookies) { - shadow.cookies.forEach((header) => { - new_cookies.push(set_cookie_parser.parseString(header)); - }); - } - - if (shadow.error) { - loaded = { - error: shadow.error - }; - } else if (shadow.redirect) { - loaded = { - redirect: shadow.redirect - }; - } else if (module.load) { - /** @type {import('types').LoadEvent} */ - const load_input = { - url: state.prerendering ? new PrerenderingURL(event.url) : new LoadURL(event.url), - params: event.params, - props: shadow.body || {}, - routeId: event.routeId, - get session() { - if (node.module.prerender ?? options.prerender.default) { - throw Error( - 'Attempted to access session from a prerendered page. Session would never be populated.' - ); - } - uses_credentials = true; - return $session; - }, - /** - * @param {RequestInfo} resource - * @param {RequestInit} opts - */ - fetch: async (resource, opts = {}) => { - /** @type {string} */ - let requested; - - if (typeof resource === 'string') { - requested = resource; - } else { - requested = resource.url; - - opts = { - method: resource.method, - headers: resource.headers, - body: resource.body, - mode: resource.mode, - credentials: resource.credentials, - cache: resource.cache, - redirect: resource.redirect, - referrer: resource.referrer, - integrity: resource.integrity, - ...opts - }; - } - - opts.headers = new Headers(opts.headers); - - // merge headers from request - for (const [key, value] of event.request.headers) { - if ( - key !== 'authorization' && - key !== 'connection' && - key !== 'cookie' && - key !== 'host' && - key !== 'if-none-match' && - !opts.headers.has(key) - ) { - opts.headers.set(key, value); - } - } - - const resolved = resolve(event.url.pathname, requested.split('?')[0]); - - /** @type {Response} */ - let response; - - /** @type {import('types').PrerenderDependency} */ - let dependency; - - // handle fetch requests for static assets. e.g. prebaked data, etc. - // we need to support everything the browser's fetch supports - const prefix = options.paths.assets || options.paths.base; - const filename = decodeURIComponent( - resolved.startsWith(prefix) ? resolved.slice(prefix.length) : resolved - ).slice(1); - const filename_html = `${filename}/index.html`; // path may also match path/index.html - - const is_asset = options.manifest.assets.has(filename); - const is_asset_html = options.manifest.assets.has(filename_html); - - if (is_asset || is_asset_html) { - const file = is_asset ? filename : filename_html; - - if (options.read) { - const type = is_asset - ? options.manifest.mimeTypes[filename.slice(filename.lastIndexOf('.'))] - : 'text/html'; - - response = new Response(options.read(file), { - headers: type ? { 'content-type': type } : {} - }); - } else { - response = await fetch( - `${event.url.origin}/${file}`, - /** @type {RequestInit} */ (opts) - ); - } - } else if (is_root_relative(resolved)) { - if (opts.credentials !== 'omit') { - uses_credentials = true; - - const authorization = event.request.headers.get('authorization'); - - // combine cookies from the initiating request with any that were - // added via set-cookie - const combined_cookies = { ...cookies }; - - for (const cookie of new_cookies) { - if (!domain_matches(event.url.hostname, cookie.domain)) continue; - if (!path_matches(resolved, cookie.path)) continue; - - combined_cookies[cookie.name] = cookie.value; - } - - const cookie = Object.entries(combined_cookies) - .map(([name, value]) => `${name}=${value}`) - .join('; '); - - if (cookie) { - opts.headers.set('cookie', cookie); - } - - if (authorization && !opts.headers.has('authorization')) { - opts.headers.set('authorization', authorization); - } - } - - if (opts.body && typeof opts.body !== 'string') { - // per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a - // Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object. - // non-string bodies are irksome to deal with, but luckily aren't particularly useful - // in this context anyway, so we take the easy route and ban them - throw new Error('Request body must be a string'); - } - - response = await respond( - new Request(new URL(requested, event.url).href, { ...opts }), - options, - { - ...state, - initiator: route - } - ); - - if (state.prerendering) { - dependency = { response, body: null }; - state.prerendering.dependencies.set(resolved, dependency); - } - } else { - // external - if (resolved.startsWith('//')) { - requested = event.url.protocol + requested; - } - - // external fetch - // allow cookie passthrough for "same-origin" - // if SvelteKit is serving my.domain.com: - // - domain.com WILL NOT receive cookies - // - my.domain.com WILL receive cookies - // - api.domain.dom WILL NOT receive cookies - // - sub.my.domain.com WILL receive cookies - // ports do not affect the resolution - // leading dot prevents mydomain.com matching domain.com - if ( - `.${new URL(requested).hostname}`.endsWith(`.${event.url.hostname}`) && - opts.credentials !== 'omit' - ) { - uses_credentials = true; - - const cookie = event.request.headers.get('cookie'); - if (cookie) opts.headers.set('cookie', cookie); - } - - // we need to delete the connection header, as explained here: - // https://github.com/nodejs/undici/issues/1470#issuecomment-1140798467 - // TODO this may be a case for being selective about which headers we let through - opts.headers.delete('connection'); - - const external_request = new Request(requested, /** @type {RequestInit} */ (opts)); - response = await options.hooks.externalFetch.call(null, external_request); - } - - const set_cookie = response.headers.get('set-cookie'); - if (set_cookie) { - new_cookies.push( - ...set_cookie_parser - .splitCookiesString(set_cookie) - .map((str) => set_cookie_parser.parseString(str)) - ); - } - - const proxy = new Proxy(response, { - get(response, key, _receiver) { - async function text() { - const body = await response.text(); - - /** @type {import('types').ResponseHeaders} */ - const headers = {}; - for (const [key, value] of response.headers) { - // TODO skip others besides set-cookie and etag? - if (key !== 'set-cookie' && key !== 'etag') { - headers[key] = value; - } - } - - if (!opts.body || typeof opts.body === 'string') { - const status_number = Number(response.status); - if (isNaN(status_number)) { - throw new Error( - `response.status is not a number. value: "${ - response.status - }" type: ${typeof response.status}` - ); - } - - fetched.push({ - url: requested, - body: opts.body, - response: { - status: status_number, - statusText: response.statusText, - headers, - body - } - }); - } - - if (dependency) { - dependency.body = body; - } - - return body; - } - - if (key === 'arrayBuffer') { - return async () => { - const buffer = await response.arrayBuffer(); - - if (dependency) { - dependency.body = new Uint8Array(buffer); - } - - // TODO should buffer be inlined into the page (albeit base64'd)? - // any conditions in which it shouldn't be? - - return buffer; - }; - } - - if (key === 'text') { - return text; - } - - if (key === 'json') { - return async () => { - return JSON.parse(await text()); - }; - } - - // TODO arrayBuffer? - - return Reflect.get(response, key, response); - } - }); - - return proxy; - }, - stuff: { ...stuff }, - status: (is_error ? status : shadow.status) ?? null, - error: is_error ? error ?? null : null - }; - - if (options.dev) { - // TODO remove this for 1.0 - Object.defineProperty(load_input, 'page', { - get: () => { - throw new Error('`page` in `load` functions has been replaced by `url` and `params`'); - } - }); - } - - loaded = normalize(await module.load.call(null, load_input)); - } else if (shadow.body) { - loaded = { - props: shadow.body - }; - } else { - loaded = {}; - } - - loaded.status = loaded.status ?? shadow.status; - - // generate __data.json files when prerendering - if (shadow.body && state.prerendering) { - const pathname = `${event.url.pathname.replace(/\/$/, '')}/__data.json`; - - const dependency = { - response: new Response(undefined), - body: JSON.stringify(shadow.body) - }; - - state.prerendering.dependencies.set(pathname, dependency); - } - - return { - node, - props: shadow.body, - loaded, - stuff: loaded.stuff || stuff, - fetched, - set_cookie_headers: new_cookies.map((new_cookie) => { - const { name, value, ...options } = new_cookie; - // @ts-expect-error - return cookie.serialize(name, value, options); - }), - uses_credentials - }; -} - -/** - * - * @param {import('types').SSRPage} route - * @param {import('types').RequestEvent} event - * @param {import('types').SSROptions} options - * @param {boolean} prerender - * @returns {Promise} - */ -async function load_shadow_data(route, event, options, prerender) { - if (!route.shadow) return {}; - - try { - const mod = await route.shadow(); - - check_method_names(mod); - - if (prerender && (mod.POST || mod.PUT || mod.DELETE || mod.PATCH)) { - throw new Error('Cannot prerender pages that have endpoints with mutative methods'); - } - - const { method } = event.request; - const is_get = method === 'HEAD' || method === 'GET'; - const handler = method === 'HEAD' ? mod.HEAD || mod.GET : mod[method]; - - if (!handler && !is_get) { - return { - status: 405, - error: new Error(`${method} method not allowed`) - }; - } - - /** @type {import('types').ShadowData} */ - const data = { - status: undefined, - cookies: [], - body: {} - }; - - if (!is_get) { - const { status, headers, body } = validate_shadow_output(await handler(event)); - add_cookies(/** @type {string[]} */ (data.cookies), headers); - data.status = status; - - // explicit errors cause an error page... - if (body instanceof Error) { - if (status < 400) { - data.status = 500; - data.error = new Error('A non-error status code was returned with an error body'); - } else { - data.error = body; - } - - return data; - } - - // ...redirects are respected... - if (status >= 300 && status < 400) { - data.redirect = /** @type {string} */ ( - headers instanceof Headers ? headers.get('location') : headers.location - ); - return data; - } - - // ...but 4xx and 5xx status codes _don't_ result in the error page - // rendering for non-GET requests — instead, we allow the page - // to render with any validation errors etc that were returned - data.body = body; - } - - const get = (method === 'HEAD' && mod.HEAD) || mod.GET; - if (get) { - const { status, headers, body } = validate_shadow_output(await get(event)); - add_cookies(/** @type {string[]} */ (data.cookies), headers); - - if (body instanceof Error) { - if (status < 400) { - data.status = 500; - data.error = new Error('A non-error status code was returned with an error body'); - } else { - data.status = status; - data.error = body; - } - - return data; - } - - if (status >= 400) { - data.status = status; - data.error = new Error('Failed to load data'); - return data; - } - - if (status >= 300) { - data.status = status; - data.redirect = /** @type {string} */ ( - headers instanceof Headers ? headers.get('location') : headers.location - ); - return data; - } - - data.body = { ...body, ...data.body }; - } - - return data; - } catch (e) { - const error = coalesce_to_error(e); - options.handle_error(error, event); - - return { - status: 500, - error - }; - } -} - -/** - * @param {string[]} target - * @param {Partial} headers - */ -function add_cookies(target, headers) { - const cookies = headers['set-cookie']; - if (cookies) { - if (Array.isArray(cookies)) { - target.push(...cookies); - } else { - target.push(/** @type {string} */ (cookies)); - } - } -} - -/** - * @param {import('types').ShadowEndpointOutput} result - */ -function validate_shadow_output(result) { - // TODO remove for 1.0 - // @ts-expect-error - if (result.fallthrough) { - throw new Error( - 'fallthrough is no longer supported. Use matchers instead: https://kit.svelte.dev/docs/routing#advanced-routing-matching' - ); - } - - const { status = 200, body = {} } = result; - let headers = result.headers || {}; - - if (headers instanceof Headers) { - if (headers.has('set-cookie')) { - throw new Error( - 'Endpoint request handler cannot use Headers interface with Set-Cookie headers' - ); - } - } else { - headers = lowercase_keys(/** @type {Record} */ (headers)); - } - - if (!is_pojo(body)) { - throw new Error( - 'Body returned from endpoint request handler must be a plain object or an Error' - ); - } - - return { status, headers, body }; -} diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 77015a8840d3..b3b237fce971 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -1,5 +1,6 @@ import devalue from 'devalue'; import { readable, writable } from 'svelte/store'; +import * as cookie from 'cookie'; import { coalesce_to_error } from '../../../utils/error.js'; import { hash } from '../../hash.js'; import { render_json_payload_script } from '../../../utils/escape.js'; @@ -7,6 +8,7 @@ import { s } from '../../../utils/misc.js'; import { Csp } from './csp.js'; import { PrerenderingURL } from '../../../utils/url.js'; import { serialize_error } from '../utils.js'; +import { HttpError } from '../../../index/private.js'; // TODO rename this function/module @@ -19,19 +21,23 @@ const updated = { * Creates the HTML response. * @param {{ * branch: Array; + * fetched: Array; + * cookies: import('set-cookie-parser').Cookie[]; * options: import('types').SSROptions; * state: import('types').SSRState; * $session: any; * page_config: { hydrate: boolean, router: boolean }; * status: number; - * error: Error | null; + * error: HttpError | Error | null; * event: import('types').RequestEvent; * resolve_opts: import('types').RequiredResolveOptions; - * stuff: Record; + * validation_errors: Record | undefined; * }} opts */ export async function render_response({ branch, + fetched, + cookies, options, state, $session, @@ -40,7 +46,7 @@ export async function render_response({ error = null, event, resolve_opts, - stuff + validation_errors }) { if (state.prerendering) { if (options.csp.mode === 'nonce') { @@ -64,33 +70,16 @@ export async function render_response({ // TODO if we add a client entry point one day, we will need to include inline_styles with the entry, otherwise stylesheets will be linked even if they are below inlineStyleThreshold const inline_styles = new Map(); - /** @type {Array} */ - const serialized_data = []; - - let shadow_props; - let rendered; - let is_private = false; - /** @type {import('types').NormalizedLoadOutputCache | undefined} */ - let cache; - - const stack = error?.stack; + const stack = error instanceof HttpError ? undefined : error?.stack; - if (options.dev && error) { + if (error && options.dev && !(error instanceof HttpError)) { error.stack = options.get_stack(error); } if (resolve_opts.ssr) { - const leaf = /** @type {import('./types.js').Loaded} */ (branch.at(-1)); - - if (leaf.loaded.status) { - // explicit status returned from `load` or a page endpoint trumps - // initial status - status = leaf.loaded.status; - } - - for (const { node, props, loaded, fetched, uses_credentials } of branch) { + for (const { node } of branch) { if (node.imports) { node.imports.forEach((url) => modulepreloads.add(url)); } @@ -102,18 +91,9 @@ export async function render_response({ if (node.inline_styles) { Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v)); } - - // TODO probably better if `fetched` wasn't populated unless `hydrate` - if (fetched && page_config.hydrate) serialized_data.push(...fetched); - if (props) shadow_props = props; - - cache = loaded?.cache; - is_private = cache?.private ?? uses_credentials; } const session = writable($session); - // Even if $session isn't accessed, it still ends up serialized in the rendered HTML - is_private = is_private || (cache?.private ?? (!!$session && Object.keys($session).length > 0)); /** @type {Record} */ const props = { @@ -129,10 +109,10 @@ export async function render_response({ params: event.params, routeId: event.routeId, status, - stuff, - url: state.prerendering ? new PrerenderingURL(event.url) : event.url + url: state.prerendering ? new PrerenderingURL(event.url) : event.url, + data: branch.reduce((acc, { data }) => (Object.assign(acc, data), acc), {}) }, - components: branch.map(({ node }) => node.module.default) + components: branch.map(({ node }) => node.component) }; // TODO remove this for 1.0 @@ -155,7 +135,11 @@ export async function render_response({ // props_n (instead of props[n]) makes it easy to avoid // unnecessary updates for layout components for (let i = 0; i < branch.length; i += 1) { - props[`props_${i}`] = await branch[i].loaded.props; + props[`data_${i}`] = branch[i].data; + } + + if (validation_errors) { + props.errors = validation_errors; } rendered = options.root.render(props); @@ -190,7 +174,7 @@ export async function render_response({ hydrate: ${resolve_opts.ssr && page_config.hydrate ? `{ status: ${status}, error: ${error && serialize_error(error, e => e.stack)}, - nodes: [${branch.map(({ node }) => node.index).join(', ')}], + node_ids: [${branch.map(({ node }) => node.index).join(', ')}], params: ${devalue(event.params)}, routeId: ${s(event.routeId)} }` : 'null'} @@ -258,19 +242,31 @@ export async function render_response({ } body += `\n\t\t`; + } + + if (resolve_opts.ssr && page_config.hydrate) { + /** @type {string[]} */ + const serialized_data = []; - body += serialized_data - .map(({ url, body, response }) => + for (const { url, body, response } of fetched) { + serialized_data.push( render_json_payload_script( { type: 'data', url, body: typeof body === 'string' ? hash(body) : undefined }, response ) - ) - .join('\n\t'); + ); + } - if (shadow_props) { - body += render_json_payload_script({ type: 'props' }, shadow_props); + if (branch.some((node) => node.server_data)) { + serialized_data.push( + render_json_payload_script( + { type: 'server_data' }, + branch.map(({ server_data }) => server_data) + ) + ); } + + body += `\n\t${serialized_data.join('\n\t')}`; } if (options.service_worker) { @@ -282,6 +278,7 @@ export async function render_response({ } if (state.prerendering) { + // TODO read headers set with setHeaders and convert into http-equiv where possible const http_equiv = []; const csp_headers = csp.csp_provider.get_meta(); @@ -289,8 +286,8 @@ export async function render_response({ http_equiv.push(csp_headers); } - if (cache) { - http_equiv.push(``); + if (state.prerendering.cache) { + http_equiv.push(``); } if (http_equiv.length > 0) { @@ -314,14 +311,6 @@ export async function render_response({ etag: `"${hash(html)}"` }); - if (link_header_preloads.size) { - headers.set('link', Array.from(link_header_preloads).join(', ')); - } - - if (cache) { - headers.set('cache-control', `${is_private ? 'private' : 'public'}, max-age=${cache.maxage}`); - } - if (!state.prerendering) { const csp_header = csp.csp_provider.get_header(); if (csp_header) { @@ -331,9 +320,19 @@ export async function render_response({ if (report_only_header) { headers.set('content-security-policy-report-only', report_only_header); } + + for (const new_cookie of cookies) { + const { name, value, ...options } = new_cookie; + // @ts-expect-error + headers.append('set-cookie', cookie.serialize(name, value, options)); + } + + if (link_header_preloads.size) { + headers.set('link', Array.from(link_header_preloads).join(', ')); + } } - if (options.dev && error) { + if (error && options.dev && !(error instanceof HttpError)) { // reset stack, otherwise it may be 'fixed' a second time error.stack = stack; } diff --git a/packages/kit/src/runtime/server/page/respond.js b/packages/kit/src/runtime/server/page/respond.js deleted file mode 100644 index 961a6b1680be..000000000000 --- a/packages/kit/src/runtime/server/page/respond.js +++ /dev/null @@ -1,275 +0,0 @@ -import { render_response } from './render.js'; -import { load_node } from './load_node.js'; -import { respond_with_error } from './respond_with_error.js'; -import { coalesce_to_error } from '../../../utils/error.js'; - -/** - * @typedef {import('./types.js').Loaded} Loaded - * @typedef {import('types').SSRNode} SSRNode - * @typedef {import('types').SSROptions} SSROptions - * @typedef {import('types').SSRState} SSRState - */ - -/** - * Gets the nodes, calls `load` for each of them, and then calls render to build the HTML response. - * @param {{ - * event: import('types').RequestEvent; - * options: SSROptions; - * state: SSRState; - * $session: any; - * resolve_opts: import('types').RequiredResolveOptions; - * route: import('types').SSRPage; - * }} opts - * @returns {Promise} - */ -export async function respond(opts) { - const { event, options, state, $session, route, resolve_opts } = opts; - - /** @type {Array} */ - let nodes; - - if (!resolve_opts.ssr) { - return await render_response({ - ...opts, - branch: [], - page_config: { - hydrate: true, - router: true - }, - status: 200, - error: null, - event, - stuff: {} - }); - } - - try { - nodes = await Promise.all( - // we use == here rather than === because [undefined] serializes as "[null]" - route.a.map((n) => (n == undefined ? n : options.manifest._.nodes[n]())) - ); - } catch (err) { - const error = coalesce_to_error(err); - - options.handle_error(error, event); - - return await respond_with_error({ - event, - options, - state, - $session, - status: 500, - error, - resolve_opts - }); - } - - // the leaf node will be present. only layouts may be undefined - const leaf = /** @type {SSRNode} */ (nodes[nodes.length - 1]).module; - - let page_config = get_page_config(leaf, options); - - if (state.prerendering) { - // if the page isn't marked as prerenderable (or is explicitly - // marked NOT prerenderable, if `prerender.default` is `true`), - // then bail out at this point - const should_prerender = leaf.prerender ?? options.prerender.default; - if (!should_prerender) { - return new Response(undefined, { - status: 204 - }); - } - } - - /** @type {Array} */ - let branch = []; - - /** @type {number} */ - let status = 200; - - /** @type {Error | null} */ - let error = null; - - /** @type {string[]} */ - let set_cookie_headers = []; - - let stuff = {}; - - ssr: { - for (let i = 0; i < nodes.length; i += 1) { - const node = nodes[i]; - - /** @type {Loaded | undefined} */ - let loaded; - - if (node) { - try { - loaded = await load_node({ - ...opts, - node, - stuff, - is_error: false, - is_leaf: i === nodes.length - 1 - }); - - set_cookie_headers = set_cookie_headers.concat(loaded.set_cookie_headers); - - if (loaded.loaded.redirect) { - return with_cookies( - new Response(undefined, { - status: loaded.loaded.status, - headers: { - location: loaded.loaded.redirect - } - }), - set_cookie_headers - ); - } - - if (loaded.loaded.error) { - error = loaded.loaded.error; - status = loaded.loaded.status ?? 500; - } - } catch (err) { - const e = coalesce_to_error(err); - - options.handle_error(e, event); - - status = 500; - error = e; - } - - if (loaded && !error) { - branch.push(loaded); - } - - if (error) { - while (i--) { - if (route.b[i]) { - const index = /** @type {number} */ (route.b[i]); - const error_node = await options.manifest._.nodes[index](); - - /** @type {Loaded} */ - let node_loaded; - let j = i; - while (!(node_loaded = branch[j])) { - j -= 1; - } - - try { - const error_loaded = /** @type {import('./types').Loaded} */ ( - await load_node({ - ...opts, - node: error_node, - stuff: node_loaded.stuff, - is_error: true, - is_leaf: false, - status, - error - }) - ); - - if (error_loaded.loaded.error) { - continue; - } - - page_config = get_page_config(error_node.module, options); - branch = branch.slice(0, j + 1).concat(error_loaded); - stuff = { ...node_loaded.stuff, ...error_loaded.stuff }; - break ssr; - } catch (err) { - const e = coalesce_to_error(err); - - options.handle_error(e, event); - - continue; - } - } - } - - // TODO backtrack until we find an __error.svelte component - // that we can use as the leaf node - // for now just return regular error page - return with_cookies( - await respond_with_error({ - event, - options, - state, - $session, - status, - error, - resolve_opts - }), - set_cookie_headers - ); - } - } - - if (loaded && loaded.loaded.stuff) { - stuff = { - ...stuff, - ...loaded.loaded.stuff - }; - } - } - } - - try { - return with_cookies( - await render_response({ - ...opts, - stuff, - event, - page_config, - status, - error, - branch: branch.filter(Boolean) - }), - set_cookie_headers - ); - } catch (err) { - const error = coalesce_to_error(err); - - options.handle_error(error, event); - - return with_cookies( - await respond_with_error({ - ...opts, - status: 500, - error - }), - set_cookie_headers - ); - } -} - -/** - * @param {import('types').SSRComponent} leaf - * @param {SSROptions} options - */ -function get_page_config(leaf, options) { - // TODO remove for 1.0 - if ('ssr' in leaf) { - throw new Error( - '`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs/hooks#handle' - ); - } - - return { - router: 'router' in leaf ? !!leaf.router : options.router, - hydrate: 'hydrate' in leaf ? !!leaf.hydrate : options.hydrate - }; -} - -/** - * @param {Response} response - * @param {string[]} set_cookie_headers - */ -function with_cookies(response, set_cookie_headers) { - if (set_cookie_headers.length) { - set_cookie_headers.forEach((value) => { - response.headers.append('set-cookie', value); - }); - } - return response; -} diff --git a/packages/kit/src/runtime/server/page/respond_with_error.js b/packages/kit/src/runtime/server/page/respond_with_error.js index c3defa2d244e..d7c14b73aea0 100644 --- a/packages/kit/src/runtime/server/page/respond_with_error.js +++ b/packages/kit/src/runtime/server/page/respond_with_error.js @@ -1,7 +1,8 @@ import { render_response } from './render.js'; -import { load_node } from './load_node.js'; +import { load_data, load_server_data } from './load_data.js'; import { coalesce_to_error } from '../../../utils/error.js'; import { GENERIC_ERROR } from '../utils.js'; +import { create_fetch } from './fetch.js'; /** * @typedef {import('./types.js').Loaded} Loaded @@ -29,50 +30,50 @@ export async function respond_with_error({ error, resolve_opts }) { + const { fetcher, fetched, cookies } = create_fetch({ + event, + options, + state, + route: GENERIC_ERROR + }); + try { const branch = []; - let stuff = {}; if (resolve_opts.ssr) { const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout - const default_error = await options.manifest._.nodes[1](); // 1 is always the root error - const layout_loaded = /** @type {Loaded} */ ( - await load_node({ - event, - options, - state, - route: GENERIC_ERROR, - node: default_layout, - $session, - stuff: {}, - is_error: false, - is_leaf: false - }) - ); + const server_data_promise = load_server_data({ + event, + node: default_layout, + parent: async () => ({}) + }); - if (layout_loaded.loaded.error) { - throw layout_loaded.loaded.error; - } + const server_data = await server_data_promise; - const error_loaded = /** @type {Loaded} */ ( - await load_node({ - event, - options, - state, - route: GENERIC_ERROR, - node: default_error, - $session, - stuff: layout_loaded ? layout_loaded.stuff : {}, - is_error: true, - is_leaf: false, - status, - error - }) - ); + const data = await load_data({ + $session, + event, + fetcher, + node: default_layout, + options, + parent: async () => ({}), + server_data_promise, + state + }); - branch.push(layout_loaded, error_loaded); - stuff = error_loaded.stuff; + branch.push( + { + node: default_layout, + server_data, + data + }, + { + node: await options.manifest._.nodes[1](), // 1 is always the root error + data: null, + server_data: null + } + ); } return await render_response({ @@ -83,12 +84,14 @@ export async function respond_with_error({ hydrate: options.hydrate, router: options.router }, - stuff, status, error, branch, + fetched, + cookies, event, - resolve_opts + resolve_opts, + validation_errors: undefined }); } catch (err) { const error = coalesce_to_error(err); diff --git a/packages/kit/src/runtime/server/page/types.d.ts b/packages/kit/src/runtime/server/page/types.d.ts index 6559f88d462f..556496ee3486 100644 --- a/packages/kit/src/runtime/server/page/types.d.ts +++ b/packages/kit/src/runtime/server/page/types.d.ts @@ -1,6 +1,7 @@ -import { JSONValue, NormalizedLoadOutput, ResponseHeaders, SSRNode, CspDirectives } from 'types'; +import { JSONValue, ResponseHeaders, SSRNode, CspDirectives } from 'types'; +import { HttpError } from '../../../index/private'; -export type Fetched = { +export interface Fetched { url: string; body?: string | null; response: { @@ -9,16 +10,18 @@ export type Fetched = { headers: ResponseHeaders; body: string; }; -}; +} + +export interface FetchState { + fetched: Fetched[]; + cookies: string[]; + new_cookies: string[]; +} export type Loaded = { node: SSRNode; - props: JSONValue | undefined; - loaded: NormalizedLoadOutput; - stuff: Record; - fetched: Fetched[]; - set_cookie_headers: string[]; - uses_credentials: boolean; + data: Record | null; + server_data: JSONValue; }; type CspMode = 'hash' | 'nonce' | 'auto'; @@ -33,3 +36,9 @@ export interface CspOpts { dev: boolean; prerender: boolean; } + +export interface SerializedHttpError extends Pick { + name: 'HttpError'; + stack: ''; + __is_http_error: true; +} diff --git a/packages/kit/src/runtime/server/utils.js b/packages/kit/src/runtime/server/utils.js index 406ad9edbcf8..27fa3abebff5 100644 --- a/packages/kit/src/runtime/server/utils.js +++ b/packages/kit/src/runtime/server/utils.js @@ -1,14 +1,4 @@ -/** @param {Record} obj */ -export function lowercase_keys(obj) { - /** @type {Record} */ - const clone = {}; - - for (const key in obj) { - clone[key.toLowerCase()] = obj[key]; - } - - return clone; -} +import { HttpError } from '../../index/private.js'; /** @param {any} body */ export function is_pojo(body) { @@ -29,22 +19,34 @@ export function is_pojo(body) { } /** - * Serialize an error into a JSON string, by copying its `name`, `message` - * and (in dev) `stack`, plus any custom properties, plus recursively - * serialized `cause` properties. This is necessary because - * `JSON.stringify(error) === '{}'` - * @param {Error} error + * Serialize an error into a JSON string through `error_to_pojo`. + * This is necessary because `JSON.stringify(error) === '{}'` + * + * @param {Error | HttpError} error * @param {(error: Error) => string | undefined} get_stack */ export function serialize_error(error, get_stack) { - return JSON.stringify(clone_error(error, get_stack)); + return JSON.stringify(error_to_pojo(error, get_stack)); } /** - * @param {Error} error + * Transform an error into a POJO, by copying its `name`, `message` + * and (in dev) `stack`, plus any custom properties, plus recursively + * serialized `cause` properties. + * Our own HttpError gets a meta property attached so we can identify it on the client. + * + * @param {HttpError | Error } error * @param {(error: Error) => string | undefined} get_stack */ -function clone_error(error, get_stack) { +export function error_to_pojo(error, get_stack) { + if (error instanceof HttpError) { + return /** @type {import('./page/types').SerializedHttpError} */ ({ + message: error.message, + status: error.status, + __is_http_error: true // TODO we should probably make this unnecessary + }); + } + const { name, message, @@ -57,7 +59,7 @@ function clone_error(error, get_stack) { /** @type {Record} */ const object = { name, message, stack: get_stack(error) }; - if (cause) object.cause = clone_error(cause, get_stack); + if (cause) object.cause = error_to_pojo(cause, get_stack); for (const key in custom) { // @ts-expect-error @@ -84,3 +86,31 @@ export function check_method_names(mod) { export const GENERIC_ERROR = { id: '__error' }; + +/** + * @param {Partial>} mod + * @param {import('types').HttpMethod} method + */ +export function method_not_allowed(mod, method) { + return new Response(`${method} method not allowed`, { + status: 405, + headers: { + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 + // "The server must generate an Allow header field in a 405 status code response" + allow: allowed_methods(mod).join(', ') + } + }); +} + +/** @param {Partial>} mod */ +export function allowed_methods(mod) { + const allowed = []; + + for (const method in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) { + if (method in mod) allowed.push(method); + } + + if (mod.GET || mod.HEAD) allowed.push('HEAD'); + + return allowed; +} diff --git a/packages/kit/src/runtime/server/utils.spec.js b/packages/kit/src/runtime/server/utils.spec.js index 2748a8b24d6c..8ec55eefe025 100644 --- a/packages/kit/src/runtime/server/utils.spec.js +++ b/packages/kit/src/runtime/server/utils.spec.js @@ -1,13 +1,6 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { lowercase_keys, serialize_error } from './utils.js'; - -test('lowercase_keys', () => { - assert.equal(lowercase_keys({ KEY: 'value' }), { key: 'value' }); - assert.equal(lowercase_keys({ Key: 'value' }), { key: 'value' }); - assert.equal(lowercase_keys({ UNDERSCORE_KEY: 'value' }), { underscore_key: 'value' }); - assert.equal(lowercase_keys({ 1: 'Hello World' }), { 1: 'Hello World' }); -}); +import { serialize_error } from './utils.js'; test('serialize_error', () => { class FancyError extends Error { diff --git a/packages/kit/src/utils/error.js b/packages/kit/src/utils/error.js index 9c00c946b43d..5290adbc2ab8 100644 --- a/packages/kit/src/utils/error.js +++ b/packages/kit/src/utils/error.js @@ -1,3 +1,5 @@ +import { HttpError, Redirect } from '../index/private.js'; + /** * @param {unknown} err * @return {Error} @@ -10,10 +12,11 @@ export function coalesce_to_error(err) { } /** - * @param {Error} err - * @param {any} errorCode - * @return {err is Error & {code: any}} + * This is an identity function that exists to make TypeScript less + * paranoid about people throwing things that aren't errors, which + * frankly is not something we should care about + * @param {unknown} error */ -export function has_error_code(err, errorCode = undefined) { - return 'code' in err && (errorCode === undefined || /** @type {any} */ (err).code === errorCode); +export function normalize_error(error) { + return /** @type {Redirect | HttpError | Error} */ (error); } diff --git a/packages/kit/src/utils/escape.spec.js b/packages/kit/src/utils/escape.spec.js index 0b9206fcae3e..1474ddadfa36 100644 --- a/packages/kit/src/utils/escape.spec.js +++ b/packages/kit/src/utils/escape.spec.js @@ -6,18 +6,22 @@ const json = suite('render_json_payload_script'); json('escapes slashes', () => { assert.equal( - render_json_payload_script({ type: 'props' }, { unsafe: '' ); }); json('escapes exclamation marks', () => { assert.equal( - render_json_payload_script({ type: 'props' }, { 'alert("xss")': 'unsafe' }), - '...-->alert("xss")': 'unsafe' } + ]), + '' ); }); diff --git a/packages/kit/src/utils/http.js b/packages/kit/src/utils/http.js index 7eea6cd42054..434a0f1f869f 100644 --- a/packages/kit/src/utils/http.js +++ b/packages/kit/src/utils/http.js @@ -1,25 +1,3 @@ -/** @param {Partial | undefined} object */ -export function to_headers(object) { - const headers = new Headers(); - - if (object) { - for (const key in object) { - const value = object[key]; - if (!value) continue; - - if (Array.isArray(value)) { - value.forEach((value) => { - headers.append(key, /** @type {string} */ (value)); - }); - } else { - headers.set(key, /** @type {string} */ (value)); - } - } - } - - return headers; -} - /** * Given an Accept header and a list of possible content types, pick * the most suitable one to respond with diff --git a/packages/kit/src/utils/http.spec.js b/packages/kit/src/utils/http.spec.js index 7e1abb277249..38e968f82f6d 100644 --- a/packages/kit/src/utils/http.spec.js +++ b/packages/kit/src/utils/http.spec.js @@ -1,30 +1,6 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { negotiate, to_headers } from './http.js'; -import { Headers } from 'undici'; - -// @ts-ignore -globalThis.Headers = Headers; - -test('handle header string value', () => { - const headers = to_headers({ name: 'value' }); - assert.equal(headers.get('name'), 'value'); -}); - -test('handle header array values', () => { - const headers = to_headers({ name: ['value1', 'value2'] }); - assert.equal(headers.get('name'), 'value1, value2'); -}); - -test('handle header int value', () => { - const headers = to_headers({ name: 123 }); - assert.equal(headers.get('name'), '123'); -}); - -test('handle header decimal value', () => { - const headers = to_headers({ name: 123.456 }); - assert.equal(headers.get('name'), '123.456'); -}); +import { negotiate } from './http.js'; test('handle valid accept header value', () => { const accept = 'text/html'; diff --git a/packages/kit/src/vite/build/build_server.js b/packages/kit/src/vite/build/build_server.js index 03cfba500ffd..d56d116ae99e 100644 --- a/packages/kit/src/vite/build/build_server.js +++ b/packages/kit/src/vite/build/build_server.js @@ -3,7 +3,7 @@ import path from 'path'; import { mkdirp, posixify } from '../../utils/filesystem.js'; import { get_vite_config, merge_vite_configs, resolve_entry } from '../utils.js'; import { load_template } from '../../core/config/index.js'; -import { get_runtime_directory } from '../../core/utils.js'; +import { runtime_directory } from '../../core/utils.js'; import { create_build, find_deps, get_default_config, is_http_method } from './utils.js'; import { s } from '../../utils/misc.js'; @@ -160,10 +160,8 @@ export async function build_server(options, client) { // add entry points for every endpoint... manifest_data.routes.forEach((route) => { - const file = route.type === 'endpoint' ? route.file : route.shadow; - - if (file) { - const resolved = path.resolve(cwd, file); + if (route.type === 'endpoint') { + const resolved = path.resolve(cwd, route.file); const relative = decodeURIComponent(path.relative(config.kit.files.routes, resolved)); const name = posixify(path.join('entries/endpoints', relative.replace(/\.js$/, ''))); input[name] = resolved; @@ -171,14 +169,18 @@ export async function build_server(options, client) { }); // ...and every component used by pages... - manifest_data.components.forEach((file) => { - const resolved = path.resolve(cwd, file); - const relative = decodeURIComponent(path.relative(config.kit.files.routes, resolved)); - - const name = relative.startsWith('..') - ? posixify(path.join('entries/fallbacks', path.basename(file))) - : posixify(path.join('entries/pages', relative)); - input[name] = resolved; + manifest_data.nodes.forEach((node) => { + for (const file of [node.component, node.shared, node.server]) { + if (file) { + const resolved = path.resolve(cwd, file); + const relative = decodeURIComponent(path.relative(config.kit.files.routes, resolved)); + + const name = relative.startsWith('..') + ? posixify(path.join('entries/fallbacks', path.basename(file))) + : posixify(path.join('entries/pages', relative.replace(/\.js$/, ''))); + input[name] = resolved; + } + } }); // ...and every matcher @@ -199,7 +201,7 @@ export async function build_server(options, client) { config, hooks: app_relative(hooks_file), has_service_worker: config.kit.serviceWorker.register && !!service_worker_entry_file, - runtime: posixify(path.relative(build_dir, get_runtime_directory(config.kit))), + runtime: posixify(path.relative(build_dir, runtime_directory)), template: load_template(cwd, config) }) ); @@ -231,23 +233,55 @@ export async function build_server(options, client) { } }); - manifest_data.components.forEach((component, i) => { - const entry = find_deps(client.vite_manifest, component, true); + manifest_data.nodes.forEach((node, i) => { + /** @type {string[]} */ + const imports = []; + + /** @type {string[]} */ + const exports = [`export const index = ${i};`]; + + /** @type {string[]} */ + const imported = []; + + /** @type {string[]} */ + const stylesheets = []; - const imports = [`import * as module from '../${vite_manifest[component].file}';`]; + if (node.component) { + const entry = find_deps(client.vite_manifest, node.component, true); + + imported.push(...entry.imports); + stylesheets.push(...entry.stylesheets); + + exports.push( + `export { default as component } from '../${vite_manifest[node.component].file}';`, + `export const file = '${entry.file}';` // TODO what is this? + ); + } + + if (node.shared) { + const entry = find_deps(client.vite_manifest, node.shared, true); + + imported.push(...entry.imports); + stylesheets.push(...entry.stylesheets); + + imports.push(`import * as shared from '../${vite_manifest[node.shared].file}';`); + exports.push(`export { shared };`); + } + + if (node.server) { + imports.push(`import * as server from '../${vite_manifest[node.server].file}';`); + exports.push(`export { server };`); + } - const exports = [ - 'export { module };', - `export const index = ${i};`, - `export const file = '${entry.file}';`, - `export const imports = ${s(entry.imports)};`, - `export const stylesheets = ${s(entry.stylesheets)};` - ]; + exports.push( + `export const imports = ${s(imported)};`, + `export const stylesheets = ${s(stylesheets)};` + ); /** @type {string[]} */ const styles = []; - entry.stylesheets.forEach((file) => { + stylesheets.forEach((file) => { if (stylesheet_lookup.has(file)) { const index = stylesheet_lookup.get(file); const name = `stylesheet_${index}`; @@ -288,7 +322,7 @@ function get_methods(cwd, output, manifest_data) { /** @type {Record} */ const methods = {}; manifest_data.routes.forEach((route) => { - const file = route.type === 'endpoint' ? route.file : route.shadow; + const file = route.type === 'endpoint' ? route.file : route.leaf.server; if (file && lookup[file]) { methods[file] = lookup[file].filter(is_http_method); diff --git a/packages/kit/src/vite/build/utils.js b/packages/kit/src/vite/build/utils.js index 523e09fbbf97..9075315f4aa5 100644 --- a/packages/kit/src/vite/build/utils.js +++ b/packages/kit/src/vite/build/utils.js @@ -1,4 +1,3 @@ -import fs from 'fs'; import * as vite from 'vite'; import { get_aliases } from '../utils.js'; @@ -102,7 +101,9 @@ export const get_default_config = function ({ config, input, ssr, outDir }) { output: { format: 'esm', entryFileNames: ssr ? '[name].js' : `${config.kit.appDir}/immutable/[name]-[hash].js`, - chunkFileNames: `${config.kit.appDir}/immutable/chunks/[name]-[hash].js`, + chunkFileNames: ssr + ? 'chunks/[name].js' + : `${config.kit.appDir}/immutable/chunks/[name]-[hash].js`, assetFileNames: `${config.kit.appDir}/immutable/assets/[name]-[hash][extname]` }, preserveEntrySignatures: 'strict' @@ -122,15 +123,7 @@ export const get_default_config = function ({ config, input, ssr, outDir }) { alias: get_aliases(config.kit) }, ssr: { - // when developing against the Kit src code, we want to ensure that - // our dependencies are bundled so that apps don't need to install - // them as peerDependencies - noExternal: process.env.BUNDLED - ? [] - : Object.keys( - JSON.parse(fs.readFileSync(new URL('../../../package.json', import.meta.url), 'utf-8')) - .devDependencies - ) + noExternal: ['@sveltejs/kit'] } }; }; diff --git a/packages/kit/src/vite/dev/index.js b/packages/kit/src/vite/dev/index.js index 97abdebc10ba..5b196823630e 100644 --- a/packages/kit/src/vite/dev/index.js +++ b/packages/kit/src/vite/dev/index.js @@ -11,7 +11,7 @@ import { parse_route_id } from '../../utils/routing.js'; import { load_template } from '../../core/config/index.js'; import { SVELTE_KIT_ASSETS } from '../../core/constants.js'; import * as sync from '../../core/sync/sync.js'; -import { get_mime_lookup, get_runtime_prefix } from '../../core/utils.js'; +import { get_mime_lookup, runtime_directory, runtime_prefix } from '../../core/utils.js'; import { get_env, prevent_illegal_vite_imports, resolve_entry } from '../utils.js'; // Vite doesn't expose this so we just copy the list for now @@ -32,16 +32,30 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) { sync.init(svelte_config, vite_config.mode); - const runtime = get_runtime_prefix(svelte_config.kit); - /** @type {import('types').Respond} */ - const respond = (await import(`${runtime}/server/index.js`)).respond; + const respond = (await import(`${runtime_prefix}/server/index.js`)).respond; + /** @type {import('types').ManifestData} */ + let manifest_data; /** @type {import('types').SSRManifest} */ let manifest; - function update_manifest() { - const { manifest_data } = sync.update(svelte_config); + const extensions = [...svelte_config.extensions, ...svelte_config.kit.moduleExtensions]; + + /** @param {string} id */ + async function resolve(id) { + const url = id.startsWith('..') ? `/@fs${path.posix.resolve(id)}` : `/${id}`; + + const module = await vite.ssrLoadModule(url); + + const module_node = await vite.moduleGraph.getModuleByUrl(url); + if (!module_node) throw new Error(`Could not find node for ${url}`); + + return { module, module_node, url }; + } + + async function update_manifest() { + ({ manifest_data } = await sync.create(svelte_config)); manifest = { appDir: svelte_config.kit.appDir, @@ -49,64 +63,95 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) { mimeTypes: get_mime_lookup(manifest_data), _: { entry: { - file: `/@fs${runtime}/client/start.js`, + file: `/@fs${runtime_prefix}/client/start.js`, imports: [], stylesheets: [] }, - nodes: manifest_data.components.map((id, index) => { + nodes: manifest_data.nodes.map((node, index) => { return async () => { - const url = id.startsWith('..') ? `/@fs${path.posix.resolve(id)}` : `/${id}`; + /** @type {import('types').SSRNode} */ + const result = {}; - const module = /** @type {import('types').SSRComponent} */ ( - await vite.ssrLoadModule(url) - ); + /** @type {import('vite').ModuleNode[]} */ + const module_nodes = []; - const node = await vite.moduleGraph.getModuleByUrl(url); - if (!node) throw new Error(`Could not find node for ${url}`); + result.index = index; - prevent_illegal_vite_imports( - node, - illegal_imports, - [...svelte_config.extensions, ...svelte_config.kit.moduleExtensions], - svelte_config.kit.outDir - ); + // these are unused in dev, it's easier to include them + result.imports = []; + result.stylesheets = []; - return { - module, - index, - file: url.endsWith('.svelte') ? url : url + '?import', - imports: [], - stylesheets: [], - // in dev we inline all styles to avoid FOUC - inline_styles: async () => { - const deps = new Set(); - await find_deps(vite, node, deps); - - /** @type {Record} */ - const styles = {}; - - for (const dep of deps) { - const parsed = new URL(dep.url, 'http://localhost/'); - const query = parsed.searchParams; - - if ( - style_pattern.test(dep.file) || - (query.has('svelte') && query.get('type') === 'style') - ) { - try { - const mod = await vite.ssrLoadModule(dep.url); - styles[dep.url] = mod.default; - } catch { - // this can happen with dynamically imported modules, I think - // because the Vite module graph doesn't distinguish between - // static and dynamic imports? TODO investigate, submit fix - } + if (node.component) { + const { module, module_node, url } = await resolve(node.component); + + module_nodes.push(module_node); + + result.component = module.default; + result.file = url.endsWith('.svelte') ? url : url + '?import'; // TODO what is this for? + + prevent_illegal_vite_imports( + module_node, + illegal_imports, + extensions, + svelte_config.kit.outDir + ); + } + + if (node.shared) { + const { module, module_node } = await resolve(node.shared); + + module_nodes.push(module_node); + + result.shared = module; + + prevent_illegal_vite_imports( + module_node, + illegal_imports, + extensions, + svelte_config.kit.outDir + ); + } + + if (node.server) { + const { module } = await resolve(node.server); + result.server = module; + } + + // in dev we inline all styles to avoid FOUC. this gets populated lazily so that + // components/stylesheets loaded via import() during `load` are included + result.inline_styles = async () => { + const deps = new Set(); + + for (const module_node of module_nodes) { + await find_deps(vite, module_node, deps); + } + + /** @type {Record} */ + const styles = {}; + + for (const dep of deps) { + const parsed = new URL(dep.url, 'http://localhost/'); + const query = parsed.searchParams; + + if ( + style_pattern.test(dep.file) || + (query.has('svelte') && query.get('type') === 'style') + ) { + try { + const mod = await vite.ssrLoadModule(dep.url); + styles[dep.url] = mod.default; + } catch { + // this can happen with dynamically imported modules, I think + // because the Vite module graph doesn't distinguish between + // static and dynamic imports? TODO investigate, submit fix } } - - return styles; } + + return styles; }; + + return result; }; }), routes: manifest_data.routes.map((route) => { @@ -119,14 +164,11 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) { pattern, names, types, - shadow: route.shadow - ? async () => { - const url = path.resolve(cwd, /** @type {string} */ (route.shadow)); - return await vite.ssrLoadModule(url); - } - : null, - a: route.a.map((id) => (id ? manifest_data.components.indexOf(id) : undefined)), - b: route.b.map((id) => (id ? manifest_data.components.indexOf(id) : undefined)) + errors: route.errors.map((id) => (id ? manifest_data.nodes.indexOf(id) : undefined)), + layouts: route.layouts.map((id) => + id ? manifest_data.nodes.indexOf(id) : undefined + ), + leaf: manifest_data.nodes.indexOf(route.leaf) }; } @@ -169,15 +211,39 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) { return error.stack ? vite.ssrRewriteStacktrace(error.stack) : error.stack; } - update_manifest(); + await update_manifest(); - for (const event of ['add', 'unlink']) { + /** + * @param {string} event + * @param {(file: string) => void} cb + */ + const watch = (event, cb) => { vite.watcher.on(event, (file) => { if (file.startsWith(svelte_config.kit.files.routes + path.sep)) { - update_manifest(); + cb(file); } }); - } + }; + /** @type {NodeJS.Timeout | null } */ + let timeout = null; + /** @param {() => void} to_run */ + const debounce = (to_run) => { + timeout && clearTimeout(timeout); + timeout = setTimeout(() => { + timeout = null; + to_run(); + }, 100); + }; + // Debounce add/unlink events because in case of folder deletion or moves + // they fire in rapid succession, causing needless invocations. + watch('add', () => debounce(update_manifest)); + watch('unlink', () => debounce(update_manifest)); + watch('change', (file) => { + // Don't run for a single file if the whole manifest is about to get updated + if (!timeout) { + sync.update(svelte_config, manifest_data, file); + } + }); const assets = svelte_config.kit.paths.assets ? SVELTE_KIT_ASSETS : svelte_config.kit.paths.base; const asset_server = sirv(svelte_config.kit.files.assets, { @@ -250,9 +316,12 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) { ); } - const runtime_base = process.env.BUNDLED - ? `/${posixify(path.relative(cwd, `${svelte_config.kit.outDir}/runtime`))}` - : `/@fs${runtime}`; + // For some reason using runtime_prefix here is buggy, since Vite will later load the modules + // again with a slightly different url (with the drive letter) on windows + const runtime_base = `/@fs${ + // Windows/Linux separation - Windows starts with a drive letter, we need a / in front there + runtime_directory.startsWith('/') ? '' : '/' + }${runtime_directory}`; const { set_private_env } = await vite.ssrLoadModule(`${runtime_base}/env-private.js`); const { set_public_env } = await vite.ssrLoadModule(`${runtime_base}/env-public.js`); diff --git a/packages/kit/src/vite/index.js b/packages/kit/src/vite/index.js index 5fc4f78d3bd0..ca03346cf334 100644 --- a/packages/kit/src/vite/index.js +++ b/packages/kit/src/vite/index.js @@ -11,7 +11,7 @@ import { build_service_worker } from './build/build_service_worker.js'; import { load_config } from '../core/config/index.js'; import { dev } from './dev/index.js'; import { generate_manifest } from '../core/generate_manifest/index.js'; -import { get_runtime_directory, logger } from '../core/utils.js'; +import { runtime_directory, logger } from '../core/utils.js'; import { find_deps, get_default_config as get_default_build_config } from './build/utils.js'; import { preview } from './preview/index.js'; import { get_aliases, resolve_entry, prevent_illegal_rollup_imports } from './utils.js'; @@ -123,20 +123,33 @@ function kit() { const input = { // Put unchanging assets in immutable directory. We don't set that in the // outDir so that other plugins can add mutable assets to the bundle - start: `${get_runtime_directory(svelte_config.kit)}/client/start.js` + start: `${runtime_directory}/client/start.js` }; - // This step is optional — Vite/Rollup will create the necessary chunks - // for everything regardless — but it means that entry chunks reflect - // their location in the source code, which is helpful for debugging - manifest_data.components.forEach((file) => { - const resolved = path.resolve(cwd, file); - const relative = decodeURIComponent(path.relative(svelte_config.kit.files.routes, resolved)); - - const name = relative.startsWith('..') - ? path.basename(file) - : posixify(path.join('pages', relative)); - input[name] = resolved; + manifest_data.nodes.forEach((node) => { + if (node.component) { + const resolved = path.resolve(cwd, node.component); + const relative = decodeURIComponent( + path.relative(svelte_config.kit.files.routes, resolved) + ); + + const name = relative.startsWith('..') + ? path.basename(node.component) + : posixify(path.join('pages', relative)); + input[`components/${name}`] = resolved; + } + + if (node.shared) { + const resolved = path.resolve(cwd, node.shared); + const relative = decodeURIComponent( + path.relative(svelte_config.kit.files.routes, resolved) + ); + + const name = relative.startsWith('..') + ? path.basename(node.shared) + : posixify(path.join('pages', relative)); + input[`modules/${name}`] = resolved; + } }); return get_default_build_config({ @@ -157,9 +170,7 @@ function kit() { fs.readFileSync(`${paths.client_out_dir}/manifest.json`, 'utf-8') ); - const entry_id = posixify( - path.relative(cwd, `${get_runtime_directory(svelte_config.kit)}/client/start.js`) - ); + const entry_id = posixify(path.relative(cwd, `${runtime_directory}/client/start.js`)); return { assets, @@ -201,7 +212,7 @@ function kit() { ]); if (is_build) { - manifest_data = sync.all(svelte_config, config_env.mode).manifest_data; + manifest_data = (await sync.all(svelte_config, config_env.mode)).manifest_data; const new_config = vite_client_build_config(); @@ -220,7 +231,7 @@ function kit() { rollupOptions: { // Vite dependency crawler needs an explicit JS entry point // eventhough server otherwise works without it - input: `${get_runtime_directory(svelte_config.kit)}/client/start.js` + input: `${runtime_directory}/client/start.js` } }, define: { @@ -295,19 +306,22 @@ function kit() { return; } - for (const file of manifest_data.components) { - const id = vite.normalizePath(path.resolve(file)); - const node = this.getModuleInfo(id); + manifest_data.nodes.forEach((_node, i) => { + const id = vite.normalizePath( + path.resolve(svelte_config.kit.outDir, `generated/nodes/${i}.js`) + ); + + const module_node = this.getModuleInfo(id); - if (node) { + if (module_node) { prevent_illegal_rollup_imports( this.getModuleInfo.bind(this), - node, + module_node, illegal_imports, svelte_config.kit.outDir ); } - } + }); const verbose = vite_config.logLevel === 'info'; log = logger({ @@ -360,12 +374,7 @@ function kit() { const results_path = `${svelte_config.kit.outDir}/generated/prerendered.json`; // do prerendering in a subprocess so any dangling stuff gets killed upon completion - const script = fileURLToPath( - new URL( - process.env.BUNDLED ? './prerender.js' : '../core/prerender/prerender.js', - import.meta.url - ) - ); + const script = fileURLToPath(new URL('../core/prerender/prerender.js', import.meta.url)); const child = fork( script, diff --git a/packages/kit/src/vite/utils.js b/packages/kit/src/vite/utils.js index 466f7d4c08de..1856c48a6d2d 100644 --- a/packages/kit/src/vite/utils.js +++ b/packages/kit/src/vite/utils.js @@ -1,7 +1,7 @@ import fs from 'fs'; import path from 'path'; import { loadConfigFromFile, loadEnv, normalizePath } from 'vite'; -import { get_runtime_directory } from '../core/utils.js'; +import { runtime_directory } from '../core/utils.js'; /** * @param {import('vite').ResolvedConfig} config @@ -104,7 +104,7 @@ export function get_aliases(config) { /** @type {import('vite').Alias[]} */ const alias = [ { find: '__GENERATED__', replacement: path.posix.join(config.outDir, 'generated') }, - { find: '$app', replacement: `${get_runtime_directory(config)}/app` }, + { find: '$app', replacement: `${runtime_directory}/app` }, // For now, we handle `$lib` specially here rather than make it a default value for // `config.kit.alias` since it has special meaning for packaging, etc. { find: '$lib', replacement: config.files.lib } @@ -128,23 +128,20 @@ export function get_aliases(config) { } } - if (!process.env.BUNDLED) { - alias.push( - { - find: '$env/static/public', - replacement: path.posix.join(config.outDir, 'runtime/env/static/public.js') - }, - { - find: '$env/static/private', - replacement: path.posix.join(config.outDir, 'runtime/env/static/private.js') - } - ); - } - - alias.push({ - find: '$env', - replacement: `${get_runtime_directory(config)}/env` - }); + alias.push( + { + find: '$env/static/public', + replacement: path.posix.join(config.outDir, 'runtime/env/static/public.js') + }, + { + find: '$env/static/private', + replacement: path.posix.join(config.outDir, 'runtime/env/static/private.js') + }, + { + find: '$env', + replacement: `${runtime_directory}/env` + } + ); return alias; } diff --git a/packages/kit/src/vite/utils.spec.js b/packages/kit/src/vite/utils.spec.js index 4dc3faad3800..fa960d8c424d 100644 --- a/packages/kit/src/vite/utils.spec.js +++ b/packages/kit/src/vite/utils.spec.js @@ -226,7 +226,7 @@ test('transform kit.alias to resolve.alias', () => { }; }) .filter( - (entry) => entry.find !== '$app' // testing this would mean to reimplement the logic in the test + (entry) => entry.find !== '$app' && entry.find !== '$env' // testing this would mean to reimplement the logic in the test ); assert.equal(transformed, [ @@ -242,10 +242,6 @@ test('transform kit.alias to resolve.alias', () => { { find: '$env/static/private', replacement: '.svelte-kit/runtime/env/static/private.js' - }, - { - find: '$env', - replacement: 'src/runtime/env' } ]); }); diff --git a/packages/kit/svelte-kit.js b/packages/kit/svelte-kit.js index bc94d37d31d6..b48389f69625 100755 --- a/packages/kit/svelte-kit.js +++ b/packages/kit/svelte-kit.js @@ -1,11 +1,2 @@ #!/usr/bin/env node -import fs from 'fs'; -import { fileURLToPath } from 'url'; - -// in our own CI, and when deploying directly from this monorepo, -// the `dist` directory will not exist yet -if (fs.existsSync(fileURLToPath(new URL('./dist', import.meta.url)))) { - import('./dist/cli.js'); -} else { - console.error('Run "pnpm build" and try running this command again'); -} +import './src/cli.js'; diff --git a/packages/kit/test/apps/amp/package.json b/packages/kit/test/apps/amp/package.json index af9154548c7e..bdcaa43af4e7 100644 --- a/packages/kit/test/apps/amp/package.json +++ b/packages/kit/test/apps/amp/package.json @@ -6,7 +6,7 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "check": "node ../../cli.js sync && tsc && svelte-check", + "check": "svelte-kit sync && tsc && svelte-check", "test": "npm run test:dev && npm run test:build", "test:dev": "cross-env DEV=true playwright test", "test:build": "playwright test" diff --git a/packages/kit/test/apps/amp/src/routes/__layout.svelte b/packages/kit/test/apps/amp/src/routes/+layout.svelte similarity index 89% rename from packages/kit/test/apps/amp/src/routes/__layout.svelte rename to packages/kit/test/apps/amp/src/routes/+layout.svelte index 6f3ce323923e..f7c91b7e246a 100644 --- a/packages/kit/test/apps/amp/src/routes/__layout.svelte +++ b/packages/kit/test/apps/amp/src/routes/+layout.svelte @@ -5,4 +5,4 @@ footer { color: purple; } - \ No newline at end of file + diff --git a/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control.svelte b/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control.svelte deleted file mode 100644 index fa919cc9f0ad..000000000000 --- a/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - -

the cache-control headers should be removed from this page

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 new file mode 100644 index 000000000000..feb67e3e4c07 --- /dev/null +++ b/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control/+page.js @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000000..63edda10a55f --- /dev/null +++ b/packages/kit/test/apps/amp/src/routes/http-equiv/cache-control/+page.svelte @@ -0,0 +1 @@ +

the cache-control headers should be removed from this page

diff --git a/packages/kit/test/apps/amp/src/routes/invalid/index.svelte b/packages/kit/test/apps/amp/src/routes/invalid/+page.svelte similarity index 100% rename from packages/kit/test/apps/amp/src/routes/invalid/index.svelte rename to packages/kit/test/apps/amp/src/routes/invalid/+page.svelte diff --git a/packages/kit/test/apps/amp/src/routes/invalid/has-stylesheet.svelte b/packages/kit/test/apps/amp/src/routes/invalid/has-stylesheet/+page.svelte similarity index 100% rename from packages/kit/test/apps/amp/src/routes/invalid/has-stylesheet.svelte rename to packages/kit/test/apps/amp/src/routes/invalid/has-stylesheet/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/same-render.js b/packages/kit/test/apps/amp/src/routes/origin.json/+server.js similarity index 53% rename from packages/kit/test/apps/basics/src/routes/shadowed/same-render.js rename to packages/kit/test/apps/amp/src/routes/origin.json/+server.js index 92af1201206c..142be28ffbca 100644 --- a/packages/kit/test/apps/basics/src/routes/shadowed/same-render.js +++ b/packages/kit/test/apps/amp/src/routes/origin.json/+server.js @@ -1,4 +1,6 @@ +import { json } from '@sveltejs/kit'; + /** @type {import('@sveltejs/kit').RequestHandler} */ export function GET({ url }) { - return { body: { url: url.toString() } }; + 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 new file mode 100644 index 000000000000..298a00a667d7 --- /dev/null +++ b/packages/kit/test/apps/amp/src/routes/origin/+page.js @@ -0,0 +1,10 @@ +/** @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 new file mode 100644 index 000000000000..21aca43ddc99 --- /dev/null +++ b/packages/kit/test/apps/amp/src/routes/origin/+page.svelte @@ -0,0 +1,10 @@ + + +

{data.origin}

+

{$page.url.origin}

+

{data.data.origin}

diff --git a/packages/kit/test/apps/amp/src/routes/origin/index.json.js b/packages/kit/test/apps/amp/src/routes/origin/index.json.js deleted file mode 100644 index c5518fd7425b..000000000000 --- a/packages/kit/test/apps/amp/src/routes/origin/index.json.js +++ /dev/null @@ -1,6 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET({ url }) { - return { - body: { origin: url.origin } - }; -} diff --git a/packages/kit/test/apps/amp/src/routes/origin/index.svelte b/packages/kit/test/apps/amp/src/routes/origin/index.svelte deleted file mode 100644 index ae0b97528978..000000000000 --- a/packages/kit/test/apps/amp/src/routes/origin/index.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - - - -

{origin}

-

{$page.url.origin}

-

{data.origin}

diff --git a/packages/kit/test/apps/amp/src/routes/styles/index.svelte b/packages/kit/test/apps/amp/src/routes/styles/+page.svelte similarity index 100% rename from packages/kit/test/apps/amp/src/routes/styles/index.svelte rename to packages/kit/test/apps/amp/src/routes/styles/+page.svelte 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 new file mode 100644 index 000000000000..714189a71ae4 --- /dev/null +++ b/packages/kit/test/apps/amp/src/routes/valid.json/+server.js @@ -0,0 +1,5 @@ +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 new file mode 100644 index 000000000000..aaf112e513dc --- /dev/null +++ b/packages/kit/test/apps/amp/src/routes/valid/+page.js @@ -0,0 +1,6 @@ +/** @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 new file mode 100644 index 000000000000..b8a04f49232a --- /dev/null +++ b/packages/kit/test/apps/amp/src/routes/valid/+page.svelte @@ -0,0 +1,16 @@ + + +

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/src/routes/valid/index.json.js b/packages/kit/test/apps/amp/src/routes/valid/index.json.js deleted file mode 100644 index 863b891d5c88..000000000000 --- a/packages/kit/test/apps/amp/src/routes/valid/index.json.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - answer: 42 - } - }; -} diff --git a/packages/kit/test/apps/amp/src/routes/valid/index.svelte b/packages/kit/test/apps/amp/src/routes/valid/index.svelte deleted file mode 100644 index a69b92bd267e..000000000000 --- a/packages/kit/test/apps/amp/src/routes/valid/index.svelte +++ /dev/null @@ -1,26 +0,0 @@ - - - - -

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

-

The answer is {answer}

-

This text is red

- - diff --git a/packages/kit/test/apps/amp/vite.config.js b/packages/kit/test/apps/amp/vite.config.js index 7d997f333156..3c59318ad7c6 100644 --- a/packages/kit/test/apps/amp/vite.config.js +++ b/packages/kit/test/apps/amp/vite.config.js @@ -1,5 +1,5 @@ import * as path from 'path'; -import { plugin } from '../../utils.js'; +import { sveltekit } from '@sveltejs/kit/vite'; /** @type {import('vite').UserConfig} */ const config = { @@ -7,7 +7,7 @@ const config = { minify: false }, clearScreen: false, - plugins: [plugin()], + plugins: [sveltekit()], server: { fs: { allow: [path.resolve('../../../src')] diff --git a/packages/kit/test/apps/basics/package.json b/packages/kit/test/apps/basics/package.json index 3375f7e65cf1..06a472f7f57d 100644 --- a/packages/kit/test/apps/basics/package.json +++ b/packages/kit/test/apps/basics/package.json @@ -6,7 +6,7 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "check": "node ../../cli.js sync && tsc && svelte-check", + "check": "svelte-kit sync && tsc && svelte-check", "test": "npm run test:dev && npm run test:build", "test:dev": "rimraf test/errors.json && cross-env DEV=true playwright test", "test:build": "rimraf test/errors.json && playwright test" diff --git a/packages/kit/test/apps/basics/src/app.d.ts b/packages/kit/test/apps/basics/src/app.d.ts index 9b0513a74bdf..9ab8200fbbc4 100644 --- a/packages/kit/test/apps/basics/src/app.d.ts +++ b/packages/kit/test/apps/basics/src/app.d.ts @@ -12,13 +12,4 @@ declare namespace App { answer?: number; calls?: number; } - - interface Stuff { - message: string; - error: string; - page: string; - value: number; - x: string; - y: string; - } } diff --git a/packages/kit/test/apps/basics/src/routes/+error.svelte b/packages/kit/test/apps/basics/src/routes/+error.svelte new file mode 100644 index 000000000000..d1897f37b61c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/+error.svelte @@ -0,0 +1,37 @@ + + + + Custom error page: {$page.error.message} + + +

{$page.status}

+ +

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

+ +
{$page.error.stack}
+ + diff --git a/packages/kit/test/apps/basics/src/routes/__layout-blank.svelte b/packages/kit/test/apps/basics/src/routes/+layout-blank.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/__layout-blank.svelte rename to packages/kit/test/apps/basics/src/routes/+layout-blank.svelte diff --git a/packages/kit/test/apps/basics/src/routes/+layout.js b/packages/kit/test/apps/basics/src/routes/+layout.js new file mode 100644 index 000000000000..0716988f42f4 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/+layout.js @@ -0,0 +1,15 @@ +import { error } from '@sveltejs/kit'; + +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch, url }) { + if (url.pathname.startsWith('/errors/error-in-layout')) { + const res = await fetch('/errors/error-in-layout/non-existent'); + throw error(res.status); + } + + return { + foo: { + bar: 'Custom layout' + } + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/+layout.svelte b/packages/kit/test/apps/basics/src/routes/+layout.svelte new file mode 100644 index 000000000000..b842834a6611 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/+layout.svelte @@ -0,0 +1,34 @@ + + + + +
{data.foo.bar}
+ + diff --git a/packages/kit/test/apps/basics/src/routes/+page.js b/packages/kit/test/apps/basics/src/routes/+page.js new file mode 100644 index 000000000000..b3c5e181aac7 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/+page.js @@ -0,0 +1,6 @@ +/** @type {import('@sveltejs/kit').Load}*/ +export async function load({ fetch }) { + const res = await fetch('/answer.json'); + const { answer } = await res.json(); + return { answer }; +} diff --git a/packages/kit/test/apps/basics/src/routes/+page.svelte b/packages/kit/test/apps/basics/src/routes/+page.svelte new file mode 100644 index 000000000000..a63bddab544e --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/+page.svelte @@ -0,0 +1,8 @@ + + +

the answer is {data.answer}

diff --git a/packages/kit/test/apps/basics/src/routes/__error.svelte b/packages/kit/test/apps/basics/src/routes/__error.svelte deleted file mode 100644 index 14726878692f..000000000000 --- a/packages/kit/test/apps/basics/src/routes/__error.svelte +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - Custom error page: {error.message} - - -

{status}

- -

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

- -
{error.stack}
- - diff --git a/packages/kit/test/apps/basics/src/routes/__layout.svelte b/packages/kit/test/apps/basics/src/routes/__layout.svelte deleted file mode 100644 index 1cb9805e3ec6..000000000000 --- a/packages/kit/test/apps/basics/src/routes/__layout.svelte +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - -
{foo.bar}
- - diff --git a/packages/kit/test/apps/basics/src/routes/accessibility/__layout.svelte b/packages/kit/test/apps/basics/src/routes/accessibility/+layout.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/accessibility/__layout.svelte rename to packages/kit/test/apps/basics/src/routes/accessibility/+layout.svelte diff --git a/packages/kit/test/apps/basics/src/routes/accessibility/a.svelte b/packages/kit/test/apps/basics/src/routes/accessibility/a/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/accessibility/a.svelte rename to packages/kit/test/apps/basics/src/routes/accessibility/a/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/accessibility/b.svelte b/packages/kit/test/apps/basics/src/routes/accessibility/b/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/accessibility/b.svelte rename to packages/kit/test/apps/basics/src/routes/accessibility/b/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/after-navigate/a.svelte b/packages/kit/test/apps/basics/src/routes/after-navigate/a/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/after-navigate/a.svelte rename to packages/kit/test/apps/basics/src/routes/after-navigate/a/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/after-navigate/b.svelte b/packages/kit/test/apps/basics/src/routes/after-navigate/b/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/after-navigate/b.svelte rename to packages/kit/test/apps/basics/src/routes/after-navigate/b/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/index.svelte b/packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/index.svelte rename to packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/anchor-afternavigate.svelte b/packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/anchor-afternavigate/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/anchor-afternavigate.svelte rename to packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/anchor-afternavigate/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/anchor-onmount.svelte b/packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/anchor-onmount/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/anchor-onmount.svelte rename to packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/anchor-onmount/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/anchor/index.svelte b/packages/kit/test/apps/basics/src/routes/anchor/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/anchor/index.svelte rename to packages/kit/test/apps/basics/src/routes/anchor/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/anchor/anchor.svelte b/packages/kit/test/apps/basics/src/routes/anchor/anchor/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/anchor/anchor.svelte rename to packages/kit/test/apps/basics/src/routes/anchor/anchor/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/answer.json.js b/packages/kit/test/apps/basics/src/routes/answer.json.js deleted file mode 100644 index 863b891d5c88..000000000000 --- a/packages/kit/test/apps/basics/src/routes/answer.json.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - answer: 42 - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/answer.json/+server.js b/packages/kit/test/apps/basics/src/routes/answer.json/+server.js new file mode 100644 index 000000000000..714189a71ae4 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/answer.json/+server.js @@ -0,0 +1,5 @@ +import { json } from '@sveltejs/kit'; + +export function GET() { + return json({ answer: 42 }); +} diff --git a/packages/kit/test/apps/basics/src/routes/asset-import/index.svelte b/packages/kit/test/apps/basics/src/routes/asset-import/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/asset-import/index.svelte rename to packages/kit/test/apps/basics/src/routes/asset-import/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/assets/index.svelte b/packages/kit/test/apps/basics/src/routes/assets/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/assets/index.svelte rename to packages/kit/test/apps/basics/src/routes/assets/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/before-navigate/a.svelte b/packages/kit/test/apps/basics/src/routes/before-navigate/a/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/before-navigate/a.svelte rename to packages/kit/test/apps/basics/src/routes/before-navigate/a/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/before-navigate/prevent-navigation.svelte b/packages/kit/test/apps/basics/src/routes/before-navigate/prevent-navigation/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/before-navigate/prevent-navigation.svelte rename to packages/kit/test/apps/basics/src/routes/before-navigate/prevent-navigation/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/caching/+page.js b/packages/kit/test/apps/basics/src/routes/caching/+page.js new file mode 100644 index 000000000000..d0d503c85bfc --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/caching/+page.js @@ -0,0 +1,6 @@ +/** @type {import('./$types').PageLoad} */ +export async function load({ setHeaders }) { + setHeaders({ + 'cache-control': 'public, max-age=30' + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/caching/+page.svelte b/packages/kit/test/apps/basics/src/routes/caching/+page.svelte new file mode 100644 index 000000000000..dab86c8207ab --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/caching/+page.svelte @@ -0,0 +1 @@ +

this page will be cached for 30 seconds

diff --git a/packages/kit/test/apps/basics/src/routes/caching/index.svelte b/packages/kit/test/apps/basics/src/routes/caching/index.svelte deleted file mode 100644 index 2f181b24c6c1..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/index.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -

this page will be cached for 30 seconds

diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/has-session.svelte b/packages/kit/test/apps/basics/src/routes/caching/private/has-session.svelte deleted file mode 100644 index aec680b6c17d..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/has-session.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - -

this page will be private even if $session is not used, but a session is return from hooks.js#getSession

diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/uses-cache-private.svelte b/packages/kit/test/apps/basics/src/routes/caching/private/uses-cache-private.svelte deleted file mode 100644 index bd760e391395..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/uses-cache-private.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - -

this page will be cached for 30 seconds

diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch.json.js b/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch.json.js deleted file mode 100644 index 863b891d5c88..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch.json.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - answer: 42 - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch.svelte b/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch.svelte deleted file mode 100644 index b3d6e4995043..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - -

this page will be cached for 30 seconds

diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-init.svelte b/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-init.svelte deleted file mode 100644 index 75d93d95612c..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-init.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - - - -

this page will be cached for 30 seconds ({session_exists})

diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-load.svelte b/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-load.svelte deleted file mode 100644 index 319fab65f41b..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-load.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - - - -

this page will be cached for 30 seconds ({session_exists})

diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest].svelte b/packages/kit/test/apps/basics/src/routes/content-type-header/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest].svelte rename to packages/kit/test/apps/basics/src/routes/content-type-header/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/content-type-header/index.svelte b/packages/kit/test/apps/basics/src/routes/content-type-header/index.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/test/apps/basics/src/routes/css/index.svelte b/packages/kit/test/apps/basics/src/routes/css/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/css/index.svelte rename to packages/kit/test/apps/basics/src/routes/css/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/css/other.svelte b/packages/kit/test/apps/basics/src/routes/css/other/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/css/other.svelte rename to packages/kit/test/apps/basics/src/routes/css/other/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/cyclical-dynamic-import/index.svelte b/packages/kit/test/apps/basics/src/routes/cyclical-dynamic-import/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/cyclical-dynamic-import/index.svelte rename to packages/kit/test/apps/basics/src/routes/cyclical-dynamic-import/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/delete-route/index.svelte b/packages/kit/test/apps/basics/src/routes/delete-route/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/delete-route/index.svelte rename to packages/kit/test/apps/basics/src/routes/delete-route/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/delete-route/[id].json.js b/packages/kit/test/apps/basics/src/routes/delete-route/[id].json.js deleted file mode 100644 index 3a268554116f..000000000000 --- a/packages/kit/test/apps/basics/src/routes/delete-route/[id].json.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function DELETE(req) { - return { - status: 200, - body: { - id: req.params.id - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/delete-route/[id].json/+server.js b/packages/kit/test/apps/basics/src/routes/delete-route/[id].json/+server.js new file mode 100644 index 000000000000..bbdde1fdd522 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/delete-route/[id].json/+server.js @@ -0,0 +1,8 @@ +import { json } from '@sveltejs/kit'; + +/** @type {import('./$types').RequestHandler} */ +export function DELETE(event) { + return json({ + id: event.params.id + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/encoded/%24[ticker]/index.svelte b/packages/kit/test/apps/basics/src/routes/encoded/%24[ticker]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/encoded/%24[ticker]/index.svelte rename to packages/kit/test/apps/basics/src/routes/encoded/%24[ticker]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/encoded/%40[username].svelte b/packages/kit/test/apps/basics/src/routes/encoded/%40[username]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/encoded/%40[username].svelte rename to packages/kit/test/apps/basics/src/routes/encoded/%40[username]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/encoded/index.svelte b/packages/kit/test/apps/basics/src/routes/encoded/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/encoded/index.svelte rename to packages/kit/test/apps/basics/src/routes/encoded/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/encoded/[slug].svelte b/packages/kit/test/apps/basics/src/routes/encoded/[slug].svelte deleted file mode 100644 index 03089c2e7a21..000000000000 --- a/packages/kit/test/apps/basics/src/routes/encoded/[slug].svelte +++ /dev/null @@ -1,25 +0,0 @@ - - - - -

dynamic

-

{path}: {slug}

-

{$page.url.pathname}: {$page.params.slug}

diff --git a/packages/kit/test/apps/basics/src/routes/encoded/[slug]/+page.js b/packages/kit/test/apps/basics/src/routes/encoded/[slug]/+page.js new file mode 100644 index 000000000000..33f0469c7b07 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/encoded/[slug]/+page.js @@ -0,0 +1,7 @@ +/** @type {import('./$types').PageLoad} */ +export function load({ url, params }) { + return { + path: url.pathname, + slug: params.slug + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/encoded/[slug]/+page.svelte b/packages/kit/test/apps/basics/src/routes/encoded/[slug]/+page.svelte new file mode 100644 index 000000000000..e6b65c7cd07a --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/encoded/[slug]/+page.svelte @@ -0,0 +1,10 @@ + + +

dynamic

+

{data.path}: {data.slug}

+

{$page.url.pathname}: {$page.params.slug}

diff --git a/packages/kit/test/apps/basics/src/routes/encoded/endpoint.js b/packages/kit/test/apps/basics/src/routes/encoded/endpoint.js deleted file mode 100644 index 4112bf5d8304..000000000000 --- a/packages/kit/test/apps/basics/src/routes/encoded/endpoint.js +++ /dev/null @@ -1,7 +0,0 @@ -export async function GET() { - return { - body: { - fruit: '🍎🍇🍌' - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/encoded/endpoint/+server.js b/packages/kit/test/apps/basics/src/routes/encoded/endpoint/+server.js new file mode 100644 index 000000000000..de73c1a17d23 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/encoded/endpoint/+server.js @@ -0,0 +1,7 @@ +import { json } from '@sveltejs/kit'; + +export async function GET() { + return json({ + fruit: '🍎🍇🍌' + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/encoded/redirect.svelte b/packages/kit/test/apps/basics/src/routes/encoded/redirect.svelte deleted file mode 100644 index 1acc12eba077..000000000000 --- a/packages/kit/test/apps/basics/src/routes/encoded/redirect.svelte +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/packages/kit/test/apps/basics/src/routes/encoded/redirect/+page.js b/packages/kit/test/apps/basics/src/routes/encoded/redirect/+page.js new file mode 100644 index 000000000000..aa5d615b50e3 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/encoded/redirect/+page.js @@ -0,0 +1,6 @@ +import { redirect } from '@sveltejs/kit'; + +/** @type {import('@sveltejs/kit').Load} */ +export function load() { + throw redirect(307, 'redirected?embedded=' + encodeURIComponent('/苗条?foo=bar&fizz=buzz')); +} diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest].ts b/packages/kit/test/apps/basics/src/routes/encoded/redirect/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest].ts rename to packages/kit/test/apps/basics/src/routes/encoded/redirect/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/encoded/redirected.svelte b/packages/kit/test/apps/basics/src/routes/encoded/redirected.svelte deleted file mode 100644 index 7e8bc13d67c7..000000000000 --- a/packages/kit/test/apps/basics/src/routes/encoded/redirected.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - - -
{embedded}
diff --git a/packages/kit/test/apps/basics/src/routes/encoded/redirected/+page.js b/packages/kit/test/apps/basics/src/routes/encoded/redirected/+page.js new file mode 100644 index 000000000000..8a941077f93b --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/encoded/redirected/+page.js @@ -0,0 +1,7 @@ +/** @type {import('@sveltejs/kit').Load} */ +export function load({ url }) { + return { + // nb: .get() on URLSearchParams does a decoding pass, so we should see the raw values. + embedded: url.searchParams.get('embedded') + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/encoded/redirected/+page.svelte b/packages/kit/test/apps/basics/src/routes/encoded/redirected/+page.svelte new file mode 100644 index 000000000000..6188626ddd17 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/encoded/redirected/+page.svelte @@ -0,0 +1,6 @@ + + +
{data.embedded}
diff --git "a/packages/kit/test/apps/basics/src/routes/encoded/\345\217\215\345\272\224.svelte" "b/packages/kit/test/apps/basics/src/routes/encoded/\345\217\215\345\272\224.svelte" deleted file mode 100644 index a31f8818f6f9..000000000000 --- "a/packages/kit/test/apps/basics/src/routes/encoded/\345\217\215\345\272\224.svelte" +++ /dev/null @@ -1,8 +0,0 @@ - diff --git "a/packages/kit/test/apps/basics/src/routes/encoded/\345\217\215\345\272\224/+page.js" "b/packages/kit/test/apps/basics/src/routes/encoded/\345\217\215\345\272\224/+page.js" new file mode 100644 index 000000000000..9a25d2560422 --- /dev/null +++ "b/packages/kit/test/apps/basics/src/routes/encoded/\345\217\215\345\272\224/+page.js" @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export function load() { + throw redirect(307, encodeURI('苗条')); +} diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...anotherrest]/index.svelte "b/packages/kit/test/apps/basics/src/routes/encoded/\345\217\215\345\272\224/+page.svelte" similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...anotherrest]/index.svelte rename to "packages/kit/test/apps/basics/src/routes/encoded/\345\217\215\345\272\224/+page.svelte" diff --git "a/packages/kit/test/apps/basics/src/routes/encoded/\350\213\227\346\235\241.svelte" "b/packages/kit/test/apps/basics/src/routes/encoded/\350\213\227\346\235\241.svelte" deleted file mode 100644 index 6ec7813eadcf..000000000000 --- "a/packages/kit/test/apps/basics/src/routes/encoded/\350\213\227\346\235\241.svelte" +++ /dev/null @@ -1,21 +0,0 @@ - - - - -

static

-

{path}

-

{$page.url.pathname}

diff --git "a/packages/kit/test/apps/basics/src/routes/encoded/\350\213\227\346\235\241/+page.js" "b/packages/kit/test/apps/basics/src/routes/encoded/\350\213\227\346\235\241/+page.js" new file mode 100644 index 000000000000..e061d65fd1ab --- /dev/null +++ "b/packages/kit/test/apps/basics/src/routes/encoded/\350\213\227\346\235\241/+page.js" @@ -0,0 +1,6 @@ +/** @type {import('@sveltejs/kit').Load} */ +export function load({ url }) { + return { + path: url.pathname + }; +} diff --git "a/packages/kit/test/apps/basics/src/routes/encoded/\350\213\227\346\235\241/+page.svelte" "b/packages/kit/test/apps/basics/src/routes/encoded/\350\213\227\346\235\241/+page.svelte" new file mode 100644 index 000000000000..88e1ef304095 --- /dev/null +++ "b/packages/kit/test/apps/basics/src/routes/encoded/\350\213\227\346\235\241/+page.svelte" @@ -0,0 +1,10 @@ + + +

static

+

{data.path}

+

{$page.url.pathname}

diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-input/sha256.js b/packages/kit/test/apps/basics/src/routes/endpoint-input/sha256/+server.js similarity index 88% rename from packages/kit/test/apps/basics/src/routes/endpoint-input/sha256.js rename to packages/kit/test/apps/basics/src/routes/endpoint-input/sha256/+server.js index 5f8f7080a83e..c80499aab343 100644 --- a/packages/kit/test/apps/basics/src/routes/endpoint-input/sha256.js +++ b/packages/kit/test/apps/basics/src/routes/endpoint-input/sha256/+server.js @@ -12,7 +12,5 @@ export async function PUT({ request }) { await new Promise((r) => setTimeout(r, 10)); } - return { - body: hash.digest('base64url') - }; + return new Response(hash.digest('base64url')); } diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/body.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/body.js deleted file mode 100644 index 86762eec3899..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/body.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return { body: {} }; -} diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/empty.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/body/+server.js similarity index 58% rename from packages/kit/test/apps/basics/src/routes/endpoint-output/empty.js rename to packages/kit/test/apps/basics/src/routes/endpoint-output/body/+server.js index b177850718ad..227b549d4d28 100644 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/empty.js +++ b/packages/kit/test/apps/basics/src/routes/endpoint-output/body/+server.js @@ -1,4 +1,6 @@ +import { json } from '@sveltejs/kit'; + /** @type {import('@sveltejs/kit').RequestHandler} */ export function GET() { - return {}; + return json({}); } diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/fetched.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/fetched.js deleted file mode 100644 index 4ad2c1f8f8e1..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/fetched.js +++ /dev/null @@ -1,8 +0,0 @@ -export function GET() { - return { - headers: { - 'x-foo': 'bar' - }, - body: 'ok' - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/headers-object.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/headers-object.js deleted file mode 100644 index ea65d4ae596b..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/headers-object.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return { - headers: new Headers({ - 'X-Foo': 'bar' - }) - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/headers.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/headers.js deleted file mode 100644 index 720f4a4a49e3..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/headers.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return { - headers: { - 'Set-Cookie': 'foo=bar' - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/null.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/null.js deleted file mode 100644 index 946c2a7b0918..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/null.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return { body: null }; -} diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/proxy.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/proxy.js deleted file mode 100644 index 02fd26bed3a6..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/proxy.js +++ /dev/null @@ -1,2 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export const GET = ({ url }) => fetch(`http://localhost:${url.searchParams.get('port')}`); diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/simple.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/simple.js deleted file mode 100644 index 863b891d5c88..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/simple.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - answer: 42 - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/stream-throw-error.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/stream-throw-error/+server.js similarity index 50% rename from packages/kit/test/apps/basics/src/routes/endpoint-output/stream-throw-error.js rename to packages/kit/test/apps/basics/src/routes/endpoint-output/stream-throw-error/+server.js index 72b52747a235..02c0024e61da 100644 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/stream-throw-error.js +++ b/packages/kit/test/apps/basics/src/routes/endpoint-output/stream-throw-error/+server.js @@ -1,13 +1,15 @@ /** @type {import('@sveltejs/kit').RequestHandler} */ export function GET() { - return { - headers: { - 'content-type': 'application/octet-stream' - }, - body: new ReadableStream({ + return new Response( + new ReadableStream({ pull() { throw Error('simulate error'); } - }) - }; + }), + { + headers: { + 'content-type': 'application/octet-stream' + } + } + ); } diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/stream-typeerror.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/stream-typeerror/+server.js similarity index 56% rename from packages/kit/test/apps/basics/src/routes/endpoint-output/stream-typeerror.js rename to packages/kit/test/apps/basics/src/routes/endpoint-output/stream-typeerror/+server.js index 0c87638fc796..bf9a11a6fb95 100644 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/stream-typeerror.js +++ b/packages/kit/test/apps/basics/src/routes/endpoint-output/stream-typeerror/+server.js @@ -1,18 +1,15 @@ -let errorName = null; +let errorName = 'null'; /** @type {import('@sveltejs/kit').RequestHandler} */ export function GET({ url }) { if (url.searchParams.has('what')) { const body = errorName; - errorName = null; - return { body }; + errorName = 'null'; + return new Response(body); } - return { - headers: { - 'content-type': 'application/octet-stream' - }, - body: new ReadableStream({ + return new Response( + new ReadableStream({ pull(controller) { controller.enqueue(42); }, @@ -20,6 +17,11 @@ export function GET({ url }) { cancel(reason) { errorName = reason?.name; } - }) - }; + }), + { + headers: { + 'content-type': 'application/octet-stream' + } + } + ); } diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/stream.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/stream/+server.js similarity index 81% rename from packages/kit/test/apps/basics/src/routes/endpoint-output/stream.js rename to packages/kit/test/apps/basics/src/routes/endpoint-output/stream/+server.js index 42d4fe0ac415..593d1b9a1253 100644 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/stream.js +++ b/packages/kit/test/apps/basics/src/routes/endpoint-output/stream/+server.js @@ -7,12 +7,8 @@ export function GET() { let length = 0; - return { - headers: { - 'content-type': 'application/octet-stream', - digest: `sha-256=${digest}` - }, - body: new ReadableStream( + return new Response( + new ReadableStream( { pull(controller) { const offset = data.byteOffset + length; @@ -31,6 +27,12 @@ export function GET() { } }, { highWaterMark: 1024 * 16 } - ) - }; + ), + { + headers: { + 'content-type': 'application/octet-stream', + digest: `sha-256=${digest}` + } + } + ); } diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/xml-bytes.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/xml-bytes.js deleted file mode 100644 index 8ccad28bf55a..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/xml-bytes.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export const GET = () => { - const body = ''; - return { - status: 200, - headers: { 'content-type': 'application/xml' }, - body: new TextEncoder().encode(body) - }; -}; diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/xml-text.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/xml-text.js deleted file mode 100644 index 6da5b073d57f..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/xml-text.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export const GET = () => { - const body = ''; - return { status: 200, headers: { 'content-type': 'application/xml' }, body }; -}; diff --git a/packages/kit/test/apps/basics/src/routes/env/index.js b/packages/kit/test/apps/basics/src/routes/env/+page.server.js similarity index 54% rename from packages/kit/test/apps/basics/src/routes/env/index.js rename to packages/kit/test/apps/basics/src/routes/env/+page.server.js index 968b678a922d..617a8542fd21 100644 --- a/packages/kit/test/apps/basics/src/routes/env/index.js +++ b/packages/kit/test/apps/basics/src/routes/env/+page.server.js @@ -1,11 +1,9 @@ import { PRIVATE_STATIC } from '$env/static/private'; import { env } from '$env/dynamic/private'; -export function GET() { +export function load() { return { - body: { - PRIVATE_STATIC, - PRIVATE_DYNAMIC: env.PRIVATE_DYNAMIC - } + PRIVATE_STATIC, + PRIVATE_DYNAMIC: env.PRIVATE_DYNAMIC }; } diff --git a/packages/kit/test/apps/basics/src/routes/env/index.svelte b/packages/kit/test/apps/basics/src/routes/env/+page.svelte similarity index 51% rename from packages/kit/test/apps/basics/src/routes/env/index.svelte rename to packages/kit/test/apps/basics/src/routes/env/+page.svelte index 4788b13e363f..4ee2f424da67 100644 --- a/packages/kit/test/apps/basics/src/routes/env/index.svelte +++ b/packages/kit/test/apps/basics/src/routes/env/+page.svelte @@ -2,15 +2,12 @@ import { PUBLIC_STATIC } from '$env/static/public'; import { env } from '$env/dynamic/public'; - /** @type {string} */ - export let PRIVATE_STATIC; - - /** @type {string} */ - export let PRIVATE_DYNAMIC; + /** @type {import('./$types').PageData} */ + export let data; -

PRIVATE_STATIC: {PRIVATE_STATIC}

-

PRIVATE_DYNAMIC: {PRIVATE_DYNAMIC}

+

PRIVATE_STATIC: {data.PRIVATE_STATIC}

+

PRIVATE_DYNAMIC: {data.PRIVATE_DYNAMIC}

PUBLIC_STATIC: {PUBLIC_STATIC}

PUBLIC_DYNAMIC: {env.PUBLIC_DYNAMIC}

diff --git a/packages/kit/test/apps/basics/src/routes/errors/clientside.svelte b/packages/kit/test/apps/basics/src/routes/errors/clientside/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/clientside.svelte rename to packages/kit/test/apps/basics/src/routes/errors/clientside/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok.json.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok.json.js deleted file mode 100644 index 6b68f98797af..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok.json.js +++ /dev/null @@ -1,6 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return { - status: 555 - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok.svelte b/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok.svelte deleted file mode 100644 index e3fad4dc5847..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - -

this text should not appear

diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok.js deleted file mode 100644 index 6b68f98797af..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok.js +++ /dev/null @@ -1,6 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return { - status: 555 - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok/+page.server.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok/+page.server.js new file mode 100644 index 000000000000..f66073b49cb3 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok/+page.server.js @@ -0,0 +1,6 @@ +import { error } from '@sveltejs/kit'; + +/** @type {import('@sveltejs/kit').RequestHandler} */ +export function load() { + throw error(555, undefined); +} diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok.svelte b/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok/+page.svelte similarity index 97% rename from packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok.svelte rename to packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok/+page.svelte index 3a5a8ea8369a..5480d57ef12c 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok.svelte +++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok/+page.svelte @@ -1,2 +1 @@ -

this text should not appear

diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow/+page.server.js similarity index 76% rename from packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow.js rename to packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow/+page.server.js index 8dc6417c8378..801e765b4132 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow.js +++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow/+page.server.js @@ -1,4 +1,4 @@ /** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { +export function load() { throw new Error('nope'); } diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow.svelte b/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow/+page.svelte similarity index 97% rename from packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow.svelte rename to packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow/+page.svelte index 3a5a8ea8369a..5480d57ef12c 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow.svelte +++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow/+page.svelte @@ -1,2 +1 @@ -

this text should not appear

diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint.json.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint.json/+server.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/endpoint.json.js rename to packages/kit/test/apps/basics/src/routes/errors/endpoint.json/+server.js diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint.svelte b/packages/kit/test/apps/basics/src/routes/errors/endpoint.svelte deleted file mode 100644 index 948899a682a3..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - -

this text should not appear

diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint/+page.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint/+page.js new file mode 100644 index 000000000000..1b9b8ea2ba2d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint/+page.js @@ -0,0 +1,9 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const res = await fetch('/errors/endpoint.json'); + if (res.ok) { + return await res.json(); + } else { + throw new Error(String(res.status)); + } +} diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint/+page.svelte b/packages/kit/test/apps/basics/src/routes/errors/endpoint/+page.svelte new file mode 100644 index 000000000000..5480d57ef12c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint/+page.svelte @@ -0,0 +1 @@ +

this text should not appear

diff --git a/packages/kit/test/apps/basics/src/routes/errors/error-in-handle.svelte b/packages/kit/test/apps/basics/src/routes/errors/error-in-handle/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/error-in-handle.svelte rename to packages/kit/test/apps/basics/src/routes/errors/error-in-handle/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/error-in-layout/index.svelte b/packages/kit/test/apps/basics/src/routes/errors/error-in-layout/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/error-in-layout/index.svelte rename to packages/kit/test/apps/basics/src/routes/errors/error-in-layout/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/init-error-endpoint.js b/packages/kit/test/apps/basics/src/routes/errors/init-error-endpoint/+server.js similarity index 50% rename from packages/kit/test/apps/basics/src/routes/errors/init-error-endpoint.js rename to packages/kit/test/apps/basics/src/routes/errors/init-error-endpoint/+server.js index 04226d63f39c..df7092c8e19d 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/init-error-endpoint.js +++ b/packages/kit/test/apps/basics/src/routes/errors/init-error-endpoint/+server.js @@ -1,10 +1,10 @@ +import { json } from '@sveltejs/kit'; + // @ts-expect-error thisvariableisnotdefined; export function GET() { - return { - body: { - answer: 42 - } - }; + return json({ + answer: 42 + }); } diff --git a/packages/kit/test/apps/basics/src/routes/errors/invalid-route-response.js b/packages/kit/test/apps/basics/src/routes/errors/invalid-route-response/+server.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/invalid-route-response.js rename to packages/kit/test/apps/basics/src/routes/errors/invalid-route-response/+server.js diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-client.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-client.svelte deleted file mode 100644 index c56e0bb9b622..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/load-client.svelte +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-client/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-client/+page.js new file mode 100644 index 000000000000..30db34ceae24 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/load-client/+page.js @@ -0,0 +1,10 @@ +import { browser } from '$app/env'; + +/** @type {import('@sveltejs/kit').Load} */ +export async function load() { + if (browser) { + throw new Error('Crashing now'); + } + + return {}; +} diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/abc.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-client/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/abc.svelte rename to packages/kit/test/apps/basics/src/routes/errors/load-client/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-client.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-error-client.svelte deleted file mode 100644 index 32ea0866cdb5..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/load-error-client.svelte +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-client/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-error-client/+page.js new file mode 100644 index 000000000000..9f6c87d99ce5 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/load-error-client/+page.js @@ -0,0 +1,10 @@ +import { error } from '@sveltejs/kit'; + +/** @type {import('./$types').PageLoad} */ +export async function load() { + if (typeof window !== 'undefined') { + throw error(555, 'Not found'); + } + + return {}; +} diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/index.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-error-client/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/index.svelte rename to packages/kit/test/apps/basics/src/routes/errors/load-error-client/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-client.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-client.svelte deleted file mode 100644 index 5c030098054b..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-client.svelte +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-server.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-server.svelte deleted file mode 100644 index e5e75921dd04..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-server.svelte +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-server.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-error-server.svelte deleted file mode 100644 index 9e0a86b05c58..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/load-error-server.svelte +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-server/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-error-server/+page.js new file mode 100644 index 000000000000..f27960c3bdec --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/load-error-server/+page.js @@ -0,0 +1,6 @@ +import { error } from '@sveltejs/kit'; + +/** @type {import('@sveltejs/kit').Load} */ +export async function load() { + throw error(555, 'Not found'); +} diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/xyz.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-error-server/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/xyz.svelte rename to packages/kit/test/apps/basics/src/routes/errors/load-error-server/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-string-client.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-error-string-client.svelte deleted file mode 100644 index 9626ed6433ce..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/load-error-string-client.svelte +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-string-server.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-error-string-server.svelte deleted file mode 100644 index 7529e27b3efe..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/load-error-string-server.svelte +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-string-server/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-error-string-server/+page.js new file mode 100644 index 000000000000..f27960c3bdec --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/load-error-string-server/+page.js @@ -0,0 +1,6 @@ +import { error } from '@sveltejs/kit'; + +/** @type {import('@sveltejs/kit').Load} */ +export async function load() { + throw error(555, 'Not found'); +} diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/index.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-error-string-server/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/index.svelte rename to packages/kit/test/apps/basics/src/routes/errors/load-error-string-server/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-server.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-server.svelte deleted file mode 100644 index de29844f579b..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/load-server.svelte +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-server/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-server/+page.js new file mode 100644 index 000000000000..0b7042387d7f --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/load-server/+page.js @@ -0,0 +1,4 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load() { + throw new Error('Crashing now'); +} diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/index.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-server/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/index.svelte rename to packages/kit/test/apps/basics/src/routes/errors/load-server/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-client.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-client.svelte deleted file mode 100644 index d4d04ba66b08..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-client.svelte +++ /dev/null @@ -1,8 +0,0 @@ - diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-client/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-client/+page.js new file mode 100644 index 000000000000..e426984dc01d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-client/+page.js @@ -0,0 +1,8 @@ +import { error } from '@sveltejs/kit'; + +export async function load() { + if (typeof window !== 'undefined') { + throw error(401, undefined); + } + return {}; +} diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[endpoint].js b/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-client/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[endpoint].js rename to packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-client/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-server.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-server.svelte deleted file mode 100644 index 6411f62eae8c..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-server.svelte +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/packages/kit/test/apps/basics/src/routes/errors/module-scope-client.svelte b/packages/kit/test/apps/basics/src/routes/errors/module-scope-client/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/module-scope-client.svelte rename to packages/kit/test/apps/basics/src/routes/errors/module-scope-client/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/module-scope-server.svelte b/packages/kit/test/apps/basics/src/routes/errors/module-scope-server/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/module-scope-server.svelte rename to packages/kit/test/apps/basics/src/routes/errors/module-scope-server/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/+error.svelte b/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/+error.svelte new file mode 100644 index 000000000000..88c8431073f9 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/+error.svelte @@ -0,0 +1,16 @@ + + +

Nested error page

+

status: {$page.status}

+

error.message: {$page.error && $page.error.message}

+

answer: {answer}

+ + diff --git a/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/+page.svelte b/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/+page.svelte new file mode 100644 index 000000000000..1a5328b38a78 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/+page.svelte @@ -0,0 +1 @@ +nope diff --git a/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/__error.svelte b/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/__error.svelte deleted file mode 100644 index cf9f99bd10c2..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/__error.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - - - -

Nested error page

-

status: {status}

-

error.message: {error && error.message}

-

answer: {answer}

- - diff --git a/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/index.svelte b/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/index.svelte deleted file mode 100644 index cab115c70cae..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/index.svelte +++ /dev/null @@ -1 +0,0 @@ -nope \ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/nope.svelte b/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/nope.svelte deleted file mode 100644 index 5b579b81f460..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/nope.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -

should not see this

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/nope/+page.js b/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/nope/+page.js new file mode 100644 index 000000000000..ba6bb3928f9f --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/nope/+page.js @@ -0,0 +1,3 @@ +export async function load() { + throw new Error('nope'); +} diff --git a/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/nope/+page.svelte b/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/nope/+page.svelte new file mode 100644 index 000000000000..c9ce7c1c8743 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/nested-error-page/nope/+page.svelte @@ -0,0 +1 @@ +

should not see this

diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/__error.svelte b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/+error.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/page-endpoint/__error.svelte rename to packages/kit/test/apps/basics/src/routes/errors/page-endpoint/+error.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/index.svelte b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/page-endpoint/index.svelte rename to packages/kit/test/apps/basics/src/routes/errors/page-endpoint/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit.js b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit.js deleted file mode 100644 index 00d7e8f697c0..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit.js +++ /dev/null @@ -1,6 +0,0 @@ -import { FancyError } from './_shared.js'; - -export const GET = () => ({ - status: 400, - body: new FancyError('oops') -}); diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit/+page.server.js b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit/+page.server.js new file mode 100644 index 000000000000..0a14dc8ba094 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit/+page.server.js @@ -0,0 +1,5 @@ +import { error } from '@sveltejs/kit'; + +export const load = () => { + throw error(400, 'oops'); +}; diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit.svelte b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit.svelte rename to packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-implicit.js b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-implicit.js deleted file mode 100644 index 48a286fccd5f..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-implicit.js +++ /dev/null @@ -1,5 +0,0 @@ -import { FancyError } from './_shared.js'; - -export const GET = () => { - throw new FancyError('oops'); -}; diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-implicit/+page.server.js b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-implicit/+page.server.js new file mode 100644 index 000000000000..8e930682739d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-implicit/+page.server.js @@ -0,0 +1,5 @@ +import { FancyError } from '../_shared.js'; + +export const load = () => { + throw new FancyError('oops'); +}; diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-implicit.svelte b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-implicit/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-implicit.svelte rename to packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-implicit/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-explicit.js b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-explicit.js deleted file mode 100644 index ce909280a54a..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-explicit.js +++ /dev/null @@ -1,6 +0,0 @@ -import { FancyError } from './_shared.js'; - -export const POST = () => ({ - status: 400, - body: new FancyError('oops') -}); diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-explicit/+page.server.js b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-explicit/+page.server.js new file mode 100644 index 000000000000..749e56f85049 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-explicit/+page.server.js @@ -0,0 +1,5 @@ +import { error } from '@sveltejs/kit'; + +export const POST = () => { + throw error(400, 'oops'); +}; diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-explicit.svelte b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-explicit/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-explicit.svelte rename to packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-explicit/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-implicit.js b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-implicit/+page.server.js similarity index 58% rename from packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-implicit.js rename to packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-implicit/+page.server.js index 6b312fa5bc00..11d6342a446f 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-implicit.js +++ b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-implicit/+page.server.js @@ -1,4 +1,4 @@ -import { FancyError } from './_shared.js'; +import { FancyError } from '../_shared.js'; export const POST = () => { throw new FancyError('oops'); diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-implicit.svelte b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-implicit/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-implicit.svelte rename to packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-implicit/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/serverside.svelte b/packages/kit/test/apps/basics/src/routes/errors/serverside/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/serverside.svelte rename to packages/kit/test/apps/basics/src/routes/errors/serverside/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/stack-trace/index.svelte b/packages/kit/test/apps/basics/src/routes/errors/stack-trace/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/errors/stack-trace/index.svelte rename to packages/kit/test/apps/basics/src/routes/errors/stack-trace/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/etag/binary.js b/packages/kit/test/apps/basics/src/routes/etag/binary.js deleted file mode 100644 index 0372de2cf649..000000000000 --- a/packages/kit/test/apps/basics/src/routes/etag/binary.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return { - headers: { - 'content-type': 'application/octet-stream' - }, - body: new Uint8Array([1, 2, 3, 4, 5]) - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/etag/custom.js b/packages/kit/test/apps/basics/src/routes/etag/custom.js deleted file mode 100644 index 64165ce521b1..000000000000 --- a/packages/kit/test/apps/basics/src/routes/etag/custom.js +++ /dev/null @@ -1,10 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET({ request }) { - if (request.headers.get('if-none-match') === '@1234@') return { status: 304 }; - return { - body: `${Math.random()}`, - headers: { - etag: '@1234@' - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/etag/text.js b/packages/kit/test/apps/basics/src/routes/etag/text.js deleted file mode 100644 index b29c8b5e6150..000000000000 --- a/packages/kit/test/apps/basics/src/routes/etag/text.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return { - body: 'some text', - headers: { - expires: 'yesterday' - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/favicon.ico.js b/packages/kit/test/apps/basics/src/routes/favicon.ico.js deleted file mode 100644 index c2db1c27cb53..000000000000 --- a/packages/kit/test/apps/basics/src/routes/favicon.ico.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - surprise: 'lol' - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/favicon.ico/+server.js b/packages/kit/test/apps/basics/src/routes/favicon.ico/+server.js new file mode 100644 index 000000000000..24f4094df805 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/favicon.ico/+server.js @@ -0,0 +1,7 @@ +import { json } from '@sveltejs/kit'; + +export function GET() { + return json({ + surprise: 'lol' + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/headers/index.svelte b/packages/kit/test/apps/basics/src/routes/headers/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/headers/index.svelte rename to packages/kit/test/apps/basics/src/routes/headers/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/headers/class.svelte b/packages/kit/test/apps/basics/src/routes/headers/class.svelte deleted file mode 100644 index 24db0bb48ea5..000000000000 --- a/packages/kit/test/apps/basics/src/routes/headers/class.svelte +++ /dev/null @@ -1,23 +0,0 @@ - - - - -

{foo}

diff --git a/packages/kit/test/apps/basics/src/routes/headers/class/+page.js b/packages/kit/test/apps/basics/src/routes/headers/class/+page.js new file mode 100644 index 000000000000..259243c2484c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/headers/class/+page.js @@ -0,0 +1,11 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const res = await fetch('/headers/echo', { + headers: new Headers({ foo: 'bar' }) + }); + + const { foo } = await res.json(); + return { + foo + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/headers/class/+page.svelte b/packages/kit/test/apps/basics/src/routes/headers/class/+page.svelte new file mode 100644 index 000000000000..1db0dad8b2dc --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/headers/class/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.foo}

diff --git a/packages/kit/test/apps/basics/src/routes/headers/echo.js b/packages/kit/test/apps/basics/src/routes/headers/echo/+server.js similarity index 80% rename from packages/kit/test/apps/basics/src/routes/headers/echo.js rename to packages/kit/test/apps/basics/src/routes/headers/echo/+server.js index e29e67125739..bedc70efb0db 100644 --- a/packages/kit/test/apps/basics/src/routes/headers/echo.js +++ b/packages/kit/test/apps/basics/src/routes/headers/echo/+server.js @@ -1,3 +1,5 @@ +import { json } from '@sveltejs/kit'; + /** @type {import('@sveltejs/kit').RequestHandler} */ export function GET({ request }) { /** @type {Record} */ @@ -8,7 +10,5 @@ export function GET({ request }) { } }); - return { - body: headers - }; + return json(headers); } diff --git a/packages/kit/test/apps/basics/src/routes/iframes/index.svelte b/packages/kit/test/apps/basics/src/routes/iframes/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/iframes/index.svelte rename to packages/kit/test/apps/basics/src/routes/iframes/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/iframes/nested/child.svelte b/packages/kit/test/apps/basics/src/routes/iframes/nested/child/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/iframes/nested/child.svelte rename to packages/kit/test/apps/basics/src/routes/iframes/nested/child/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/iframes/nested/parent.svelte b/packages/kit/test/apps/basics/src/routes/iframes/nested/parent/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/iframes/nested/parent.svelte rename to packages/kit/test/apps/basics/src/routes/iframes/nested/parent/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/imports/+page.svelte b/packages/kit/test/apps/basics/src/routes/imports/+page.svelte new file mode 100644 index 000000000000..f09d461a28b8 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/imports/+page.svelte @@ -0,0 +1 @@ +markdown diff --git a/packages/kit/test/apps/basics/src/routes/imports/index.svelte b/packages/kit/test/apps/basics/src/routes/imports/index.svelte deleted file mode 100644 index 86041a8afd43..000000000000 --- a/packages/kit/test/apps/basics/src/routes/imports/index.svelte +++ /dev/null @@ -1 +0,0 @@ -markdown \ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/imports/markdown.svelte b/packages/kit/test/apps/basics/src/routes/imports/markdown/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/imports/markdown.svelte rename to packages/kit/test/apps/basics/src/routes/imports/markdown/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/index.svelte b/packages/kit/test/apps/basics/src/routes/index.svelte deleted file mode 100644 index c0d95fa284ae..000000000000 --- a/packages/kit/test/apps/basics/src/routes/index.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - - - -

the answer is {answer}

diff --git a/packages/kit/test/apps/basics/src/routes/keepfocus/index.svelte b/packages/kit/test/apps/basics/src/routes/keepfocus/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/keepfocus/index.svelte rename to packages/kit/test/apps/basics/src/routes/keepfocus/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/load/+page.js b/packages/kit/test/apps/basics/src/routes/load/+page.js new file mode 100644 index 000000000000..069608981910 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/+page.js @@ -0,0 +1,9 @@ +import { browser } from '$app/env'; + +/** @type {import('@sveltejs/kit').Load} */ +export function load(pageContext) { + if (browser) { + window.pageContext = pageContext; + } + return { foo: 'bar' }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/index.svelte b/packages/kit/test/apps/basics/src/routes/load/+page.svelte similarity index 53% rename from packages/kit/test/apps/basics/src/routes/load/index.svelte rename to packages/kit/test/apps/basics/src/routes/load/+page.svelte index 181c11684ed2..1685c738ef3e 100644 --- a/packages/kit/test/apps/basics/src/routes/load/index.svelte +++ b/packages/kit/test/apps/basics/src/routes/load/+page.svelte @@ -1,24 +1,9 @@ - - -

bar == {foo}?

+

bar == {data.foo}?

fetch request fetch relative diff --git a/packages/kit/test/apps/basics/src/routes/load/[dynamic].json.js b/packages/kit/test/apps/basics/src/routes/load/[dynamic].json/+server.js similarity index 52% rename from packages/kit/test/apps/basics/src/routes/load/[dynamic].json.js rename to packages/kit/test/apps/basics/src/routes/load/[dynamic].json/+server.js index 7a2cf9a482d8..136104497fe7 100644 --- a/packages/kit/test/apps/basics/src/routes/load/[dynamic].json.js +++ b/packages/kit/test/apps/basics/src/routes/load/[dynamic].json/+server.js @@ -1,8 +1,8 @@ +import { json } from '@sveltejs/kit'; + /** @type {import('@sveltejs/kit').RequestHandler} */ export function GET({ params }) { - return { - body: { - name: params.dynamic - } - }; + return json({ + name: params.dynamic + }); } diff --git a/packages/kit/test/apps/basics/src/routes/load/[dynamic].svelte b/packages/kit/test/apps/basics/src/routes/load/[dynamic].svelte deleted file mode 100644 index fb4e204f5e70..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/[dynamic].svelte +++ /dev/null @@ -1,16 +0,0 @@ - - - - -

{name}

diff --git a/packages/kit/test/apps/basics/src/routes/load/[dynamic]/+page.js b/packages/kit/test/apps/basics/src/routes/load/[dynamic]/+page.js new file mode 100644 index 000000000000..315414aeaaa9 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/[dynamic]/+page.js @@ -0,0 +1,5 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ params, fetch }) { + const res = await fetch(`/load/${params.dynamic}.json`); + return await res.json(); +} diff --git a/packages/kit/test/apps/basics/src/routes/load/[dynamic]/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/[dynamic]/+page.svelte new file mode 100644 index 000000000000..a5a0601b3042 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/[dynamic]/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.name}

diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/+layout.js b/packages/kit/test/apps/basics/src/routes/load/change-detection/+layout.js new file mode 100644 index 000000000000..09db5461edd6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/change-detection/+layout.js @@ -0,0 +1,16 @@ +let count = 0; + +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch, depends }) { + const res = await fetch('/load/change-detection/data.json'); + const { type } = await res.json(); + + count += 1; + + depends('custom:change-detection-layout'); + + return { + type, + loads: count + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/+layout.svelte b/packages/kit/test/apps/basics/src/routes/load/change-detection/+layout.svelte new file mode 100644 index 000000000000..8ba1567c45c9 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/change-detection/+layout.svelte @@ -0,0 +1,7 @@ + + +

{data.type} loads: {data.loads}

+ diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/__layout.svelte b/packages/kit/test/apps/basics/src/routes/load/change-detection/__layout.svelte deleted file mode 100644 index 4ff23edb4de9..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/change-detection/__layout.svelte +++ /dev/null @@ -1,31 +0,0 @@ - - - - -

{type} loads: {loads}

- diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/data.json.js b/packages/kit/test/apps/basics/src/routes/load/change-detection/data.json.js deleted file mode 100644 index cf49417693de..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/change-detection/data.json.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - type: 'layout' - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/data.json/+server.js b/packages/kit/test/apps/basics/src/routes/load/change-detection/data.json/+server.js new file mode 100644 index 000000000000..4917a5dac6cf --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/change-detection/data.json/+server.js @@ -0,0 +1,7 @@ +import { json } from '@sveltejs/kit'; + +export function GET() { + return json({ + type: 'layout' + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/one/[x].svelte b/packages/kit/test/apps/basics/src/routes/load/change-detection/one/[x].svelte deleted file mode 100644 index 83f281b3615d..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/change-detection/one/[x].svelte +++ /dev/null @@ -1,38 +0,0 @@ - - - - -

x: {x}: {loads}

- - diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/one/[x]/+page.js b/packages/kit/test/apps/basics/src/routes/load/change-detection/one/[x]/+page.js new file mode 100644 index 000000000000..30ea122f53a7 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/change-detection/one/[x]/+page.js @@ -0,0 +1,19 @@ +import { browser } from '$app/env'; + +let count = 0; + +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ params, setHeaders }) { + if (browser) { + count += 1; + } + + // Using setHeader has no effect on the client; testing that here + setHeaders({ + 'cache-control': 'public, max-age=5' + }); + return { + x: params.x, + loads: count + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/one/[x]/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/change-detection/one/[x]/+page.svelte new file mode 100644 index 000000000000..9acf3f2212f9 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/change-detection/one/[x]/+page.svelte @@ -0,0 +1,24 @@ + + +

x: {data.x}: {data.loads}

+ + + + diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/session/unused.svelte b/packages/kit/test/apps/basics/src/routes/load/change-detection/session/unused.svelte deleted file mode 100644 index 1189bedaff07..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/change-detection/session/unused.svelte +++ /dev/null @@ -1,23 +0,0 @@ - - - - -

{count}

- diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/session/unused/+page.js b/packages/kit/test/apps/basics/src/routes/load/change-detection/session/unused/+page.js new file mode 100644 index 000000000000..565755ef01e6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/change-detection/session/unused/+page.js @@ -0,0 +1,7 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load() { + return { + // Needs to be an object, else Svelte will do by-value-comparison and skip rerender + obj: {} + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/session/unused/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/change-detection/session/unused/+page.svelte new file mode 100644 index 000000000000..2621e4656291 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/change-detection/session/unused/+page.svelte @@ -0,0 +1,12 @@ + + +

{count}

+ diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/session/used.svelte b/packages/kit/test/apps/basics/src/routes/load/change-detection/session/used.svelte deleted file mode 100644 index a473de611056..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/change-detection/session/used.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - - - -

{count}

- diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/session/used/+page.js b/packages/kit/test/apps/basics/src/routes/load/change-detection/session/used/+page.js new file mode 100644 index 000000000000..181c710e1efa --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/change-detection/session/used/+page.js @@ -0,0 +1,8 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ session }) { + session; + return { + // Needs to be an object, else Svelte will do by-value-comparison and skip rerender + obj: {} + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/session/used/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/change-detection/session/used/+page.svelte new file mode 100644 index 000000000000..2621e4656291 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/change-detection/session/used/+page.svelte @@ -0,0 +1,12 @@ + + +

{count}

+ diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/two/[y].svelte b/packages/kit/test/apps/basics/src/routes/load/change-detection/two/[y].svelte deleted file mode 100644 index 7e451ced0bd4..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/change-detection/two/[y].svelte +++ /dev/null @@ -1,26 +0,0 @@ - - - - -

y: {y}: {loads}

diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/two/[y]/+page.js b/packages/kit/test/apps/basics/src/routes/load/change-detection/two/[y]/+page.js new file mode 100644 index 000000000000..044f6650e74c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/change-detection/two/[y]/+page.js @@ -0,0 +1,14 @@ +let count = 0; + +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ params, setHeaders }) { + count += 1; + + setHeaders({ + 'cache-control': 'max-age=5' + }); + return { + y: params.y, + loads: count + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/change-detection/two/[y]/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/change-detection/two/[y]/+page.svelte new file mode 100644 index 000000000000..bbce9d9b9f8c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/change-detection/two/[y]/+page.svelte @@ -0,0 +1,6 @@ + + +

y: {data.y}: {data.loads}

diff --git a/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/+page.js b/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/+page.js new file mode 100644 index 000000000000..f05fdec6a15c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/+page.js @@ -0,0 +1,5 @@ +export async function load() { + return { + Thing: (await import('./_/Thing.svelte')).default + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/+page.svelte new file mode 100644 index 000000000000..d2a75179b0b2 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/index.svelte b/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/index.svelte deleted file mode 100644 index 90167d792806..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/index.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.json.js b/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.json.js deleted file mode 100644 index cf73c2500f44..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.json.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET(request) { - return { - body: { - name: request.locals.name - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.json/+server.js b/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.json/+server.js new file mode 100644 index 000000000000..7536c51d8bd1 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.json/+server.js @@ -0,0 +1,8 @@ +import { json } from '@sveltejs/kit'; + +/** @type {import('./$types').RequestHandler} */ +export function GET(event) { + return json({ + name: event.locals.name + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.svelte b/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.svelte deleted file mode 100644 index 11350ea7200c..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - -

Hello {name}!

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed/+page.js b/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed/+page.js new file mode 100644 index 000000000000..b806f33bc33a --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed/+page.js @@ -0,0 +1,11 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const res = await fetch('/load/fetch-credentialed.json'); + + /** @type {any} */ + const { name } = await res.json(); + + return { + name + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed/+page.svelte new file mode 100644 index 000000000000..e6ca555a140c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed/+page.svelte @@ -0,0 +1,6 @@ + + +

Hello {data.name}!

diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-headers.json.js b/packages/kit/test/apps/basics/src/routes/load/fetch-headers.json/+server.js similarity index 78% rename from packages/kit/test/apps/basics/src/routes/load/fetch-headers.json.js rename to packages/kit/test/apps/basics/src/routes/load/fetch-headers.json/+server.js index 135aa32b2677..28608043de70 100644 --- a/packages/kit/test/apps/basics/src/routes/load/fetch-headers.json.js +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-headers.json/+server.js @@ -1,3 +1,5 @@ +import { json } from '@sveltejs/kit'; + /** @type {import('@sveltejs/kit').RequestHandler} */ export function GET({ request }) { /** @type {Record} */ @@ -7,7 +9,5 @@ export function GET({ request }) { body[key] = value; }); - return { - body - }; + return json(body); } diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-headers.svelte b/packages/kit/test/apps/basics/src/routes/load/fetch-headers.svelte deleted file mode 100644 index 9cc8544c6215..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/fetch-headers.svelte +++ /dev/null @@ -1,27 +0,0 @@ - - - - -
{json}
diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-headers/+page.js b/packages/kit/test/apps/basics/src/routes/load/fetch-headers/+page.js new file mode 100644 index 000000000000..65f31059ef45 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-headers/+page.js @@ -0,0 +1,8 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const res = await fetch('/load/fetch-headers.json'); + + return { + headers: await res.json() + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-headers/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/fetch-headers/+page.svelte new file mode 100644 index 000000000000..27cbe80d0ce9 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-headers/+page.svelte @@ -0,0 +1,14 @@ + + +
{json}
diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-relative.json.js b/packages/kit/test/apps/basics/src/routes/load/fetch-relative.json.js deleted file mode 100644 index f5d43f9b9df5..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/fetch-relative.json.js +++ /dev/null @@ -1,16 +0,0 @@ -export function GET() { - return { - body: { - answer: 42 - } - }; -} - -/** @type {import('@sveltejs/kit').RequestHandler} */ -export async function POST({ request }) { - return { - body: { - question: await request.text() - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-relative.json/+server.js b/packages/kit/test/apps/basics/src/routes/load/fetch-relative.json/+server.js new file mode 100644 index 000000000000..bd408dca0d38 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-relative.json/+server.js @@ -0,0 +1,15 @@ +import { json } from '@sveltejs/kit'; + +/** @type {import('./$types').RequestHandler} */ +export function GET() { + return json({ + answer: 42 + }); +} + +/** @type {import('./$types').RequestHandler} */ +export async function POST({ request }) { + return json({ + question: await request.text() + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-relative.svelte b/packages/kit/test/apps/basics/src/routes/load/fetch-relative.svelte deleted file mode 100644 index 4418226688cc..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/fetch-relative.svelte +++ /dev/null @@ -1,19 +0,0 @@ - - - - -

the answer is {answer}

-

the question was {question}

diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-relative/+page.js b/packages/kit/test/apps/basics/src/routes/load/fetch-relative/+page.js new file mode 100644 index 000000000000..b010fe96432c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-relative/+page.js @@ -0,0 +1,7 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const get = await fetch('fetch-relative.json'); + const post = await fetch('fetch-relative.json', { method: 'post', body: '?' }); + + return { ...(await get.json()), ...(await post.json()) }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-relative/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/fetch-relative/+page.svelte new file mode 100644 index 000000000000..0dc51f657e36 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-relative/+page.svelte @@ -0,0 +1,6 @@ + + +

the answer is {data.answer}

+

the question was {data.question}

diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-request.json.js b/packages/kit/test/apps/basics/src/routes/load/fetch-request.json.js deleted file mode 100644 index 863b891d5c88..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/fetch-request.json.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - answer: 42 - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-request.json/+server.js b/packages/kit/test/apps/basics/src/routes/load/fetch-request.json/+server.js new file mode 100644 index 000000000000..c0e87e3803a5 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-request.json/+server.js @@ -0,0 +1,7 @@ +import { json } from '@sveltejs/kit'; + +export function GET() { + return json({ + answer: 42 + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-request.svelte b/packages/kit/test/apps/basics/src/routes/load/fetch-request.svelte deleted file mode 100644 index 6f012d3afded..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/fetch-request.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - - - -

the answer is {answer}

diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-request/+page.js b/packages/kit/test/apps/basics/src/routes/load/fetch-request/+page.js new file mode 100644 index 000000000000..5c13157f083e --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-request/+page.js @@ -0,0 +1,14 @@ +import { browser } from '$app/env'; + +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const url = '/load/fetch-request.json'; + + // this is contrived, but saves us faffing about with node-fetch here + const resource = browser ? new Request(url) : url; + + const res = await fetch(resource); + const { answer } = await res.json(); + + return { answer }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-request/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/fetch-request/+page.svelte new file mode 100644 index 000000000000..c890e7840e38 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-request/+page.svelte @@ -0,0 +1,6 @@ + + +

the answer is {data.answer}

diff --git a/packages/kit/test/apps/basics/src/routes/load/large-response/+page.js b/packages/kit/test/apps/basics/src/routes/load/large-response/+page.js new file mode 100644 index 000000000000..e20728bec89e --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/large-response/+page.js @@ -0,0 +1,9 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const res = await fetch('/load/large-response/text.txt'); + const text = await res.text(); + + return { + text + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/large-response/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/large-response/+page.svelte new file mode 100644 index 000000000000..82d2ea2fb6fd --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/large-response/+page.svelte @@ -0,0 +1,6 @@ + + +

text.length is {data.text.length}

diff --git a/packages/kit/test/apps/basics/src/routes/load/large-response/index.svelte b/packages/kit/test/apps/basics/src/routes/load/large-response/index.svelte deleted file mode 100644 index 20f6fff18c83..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/large-response/index.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - - - -

text.length is {text.length}

diff --git a/packages/kit/test/apps/basics/src/routes/load/large-response/text.txt.js b/packages/kit/test/apps/basics/src/routes/load/large-response/text.txt/+server.js similarity index 88% rename from packages/kit/test/apps/basics/src/routes/load/large-response/text.txt.js rename to packages/kit/test/apps/basics/src/routes/load/large-response/text.txt/+server.js index 6bd60c5b1df0..ca531bac7066 100644 --- a/packages/kit/test/apps/basics/src/routes/load/large-response/text.txt.js +++ b/packages/kit/test/apps/basics/src/routes/load/large-response/text.txt/+server.js @@ -11,8 +11,8 @@ const encoder = new TextEncoder(); export function GET() { let i = 0; - return { - body: new ReadableStream({ + return new Response( + new ReadableStream({ pull: (controller) => { if (i++ < chunk_count) { controller.enqueue(encoder.encode(chunk)); @@ -21,5 +21,5 @@ export function GET() { } } }) - }; + ); } diff --git a/packages/kit/test/apps/basics/src/routes/load/layout-props/+layout.js b/packages/kit/test/apps/basics/src/routes/load/layout-props/+layout.js new file mode 100644 index 000000000000..33cd8304ce77 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/layout-props/+layout.js @@ -0,0 +1,8 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ session }) { + session; // not necessary, but prevents the argument from being marked as unused + return { + // Needs to be an object, else Svelte will do by-value-comparison and skip rerender + obj: {} + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/layout-props/+layout.svelte b/packages/kit/test/apps/basics/src/routes/load/layout-props/+layout.svelte new file mode 100644 index 000000000000..b6c696ba75cb --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/layout-props/+layout.svelte @@ -0,0 +1,13 @@ + + +

{count}

+ + diff --git a/packages/kit/test/apps/basics/src/routes/load/layout-props/__layout.svelte b/packages/kit/test/apps/basics/src/routes/load/layout-props/__layout.svelte deleted file mode 100644 index 555061eead4b..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/layout-props/__layout.svelte +++ /dev/null @@ -1,26 +0,0 @@ - - - - -

{count}

- - diff --git a/packages/kit/test/apps/basics/src/routes/load/layout-props/a.svelte b/packages/kit/test/apps/basics/src/routes/load/layout-props/a/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/load/layout-props/a.svelte rename to packages/kit/test/apps/basics/src/routes/load/layout-props/a/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/load/layout-props/b.svelte b/packages/kit/test/apps/basics/src/routes/load/layout-props/b/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/load/layout-props/b.svelte rename to packages/kit/test/apps/basics/src/routes/load/layout-props/b/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/load/parent/+layout.js b/packages/kit/test/apps/basics/src/routes/load/parent/+layout.js new file mode 100644 index 000000000000..c40d1db32936 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/parent/+layout.js @@ -0,0 +1,6 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load() { + return { + message: 'original' + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/parent/+layout.svelte b/packages/kit/test/apps/basics/src/routes/load/parent/+layout.svelte new file mode 100644 index 000000000000..13e0e91ed627 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/parent/+layout.svelte @@ -0,0 +1 @@ + diff --git a/packages/kit/test/apps/basics/src/routes/load/parent/[x]/+layout.js b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/+layout.js new file mode 100644 index 000000000000..2f065463d856 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/+layout.js @@ -0,0 +1,8 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ params, parent }) { + const data = await parent(); + return { + message: `${data.message} + new`, + x: params.x + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/parent/[x]/+layout.svelte b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/+layout.svelte new file mode 100644 index 000000000000..4fa864ce7aa9 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/+layout.svelte @@ -0,0 +1 @@ + diff --git a/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/+layout.js b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/+layout.js new file mode 100644 index 000000000000..d96610b162ac --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/+layout.js @@ -0,0 +1,6 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ params }) { + return { + y: params.y + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/+layout.svelte b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/+layout.svelte new file mode 100644 index 000000000000..4fa864ce7aa9 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/+layout.svelte @@ -0,0 +1 @@ + diff --git a/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/[z]/+page.js b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/[z]/+page.js new file mode 100644 index 000000000000..748a7acbfcc1 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/[z]/+page.js @@ -0,0 +1,6 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ params }) { + return { + z: params.z + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/[z]/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/[z]/+page.svelte new file mode 100644 index 000000000000..0b85507c19d8 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/[z]/+page.svelte @@ -0,0 +1,6 @@ + + +

message: {$page.data.message}

+
{JSON.stringify($page.data)}
diff --git a/packages/kit/test/apps/basics/src/routes/load/props/__layout.svelte b/packages/kit/test/apps/basics/src/routes/load/props/+layout.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/load/props/__layout.svelte rename to packages/kit/test/apps/basics/src/routes/load/props/+layout.svelte diff --git a/packages/kit/test/apps/basics/src/routes/load/props/+page.js b/packages/kit/test/apps/basics/src/routes/load/props/+page.js new file mode 100644 index 000000000000..b0217e445670 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/props/+page.js @@ -0,0 +1,4 @@ +export async function load() { + const message = 'Hello from Index!'; + return { message }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/props/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/props/+page.svelte new file mode 100644 index 000000000000..b81ed61647bc --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/props/+page.svelte @@ -0,0 +1,7 @@ + + +

Index

+ +

Data: {data.message}

diff --git a/packages/kit/test/apps/basics/src/routes/load/props/about.svelte b/packages/kit/test/apps/basics/src/routes/load/props/about/+page.svelte similarity index 51% rename from packages/kit/test/apps/basics/src/routes/load/props/about.svelte rename to packages/kit/test/apps/basics/src/routes/load/props/about/+page.svelte index 6b5b14c6a9bf..ee6be11dcbea 100644 --- a/packages/kit/test/apps/basics/src/routes/load/props/about.svelte +++ b/packages/kit/test/apps/basics/src/routes/load/props/about/+page.svelte @@ -1,5 +1,5 @@

About

diff --git a/packages/kit/test/apps/basics/src/routes/load/props/index.svelte b/packages/kit/test/apps/basics/src/routes/load/props/index.svelte deleted file mode 100644 index d45892aa3872..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/props/index.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - - - -

Index

- -

Data: {data}

diff --git a/packages/kit/test/apps/basics/src/routes/load/raw-body.json.js b/packages/kit/test/apps/basics/src/routes/load/raw-body.json.js deleted file mode 100644 index 4498099d6a66..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/raw-body.json.js +++ /dev/null @@ -1,12 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export async function POST({ request }) { - const text = await request.text(); - const json = JSON.parse(text); - - return { - body: { - body: json, - rawBody: text - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/load/raw-body.json/+server.js b/packages/kit/test/apps/basics/src/routes/load/raw-body.json/+server.js new file mode 100644 index 000000000000..142c35ec6884 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/raw-body.json/+server.js @@ -0,0 +1,12 @@ +import { json } from '@sveltejs/kit'; + +/** @type {import('@sveltejs/kit').RequestHandler} */ +export async function POST({ request }) { + const rawBody = await request.text(); + const body = JSON.parse(rawBody); + + return json({ + body, + rawBody + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/load/raw-body.svelte b/packages/kit/test/apps/basics/src/routes/load/raw-body.svelte deleted file mode 100644 index 07bc6279ee67..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/raw-body.svelte +++ /dev/null @@ -1,27 +0,0 @@ - - - - -
{JSON.stringify(body)}
-
{rawBody}
\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/load/raw-body/+page.js b/packages/kit/test/apps/basics/src/routes/load/raw-body/+page.js new file mode 100644 index 000000000000..f6d3d5c7428c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/raw-body/+page.js @@ -0,0 +1,12 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const res = await fetch('/load/raw-body.json', { + method: 'POST', + headers: { + 'content-type': 'application/json' + }, + body: '{ "oddly" : { "formatted" : "json" } }' + }); + + return await res.json(); +} diff --git a/packages/kit/test/apps/basics/src/routes/load/raw-body/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/raw-body/+page.svelte new file mode 100644 index 000000000000..5cdd56782346 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/raw-body/+page.svelte @@ -0,0 +1,7 @@ + + +
{JSON.stringify(data.body)}
+
{data.rawBody}
diff --git a/packages/kit/test/apps/basics/src/routes/load/relay.json.js b/packages/kit/test/apps/basics/src/routes/load/relay.json.js deleted file mode 100644 index 8eb052d61f5c..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/relay.json.js +++ /dev/null @@ -1,8 +0,0 @@ -export function GET() { - return { - headers: { - 'content-type': 'application/json' - }, - body: '42' - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/load/relay.json/+server.js b/packages/kit/test/apps/basics/src/routes/load/relay.json/+server.js new file mode 100644 index 000000000000..69fd27bb7cae --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/relay.json/+server.js @@ -0,0 +1,5 @@ +import { json } from '@sveltejs/kit'; + +export function GET() { + return json('42'); +} diff --git a/packages/kit/test/apps/basics/src/routes/load/relay.svelte b/packages/kit/test/apps/basics/src/routes/load/relay.svelte deleted file mode 100644 index 67979d6db666..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/relay.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - - -

{answer}

diff --git a/packages/kit/test/apps/basics/src/routes/load/relay/+page.js b/packages/kit/test/apps/basics/src/routes/load/relay/+page.js new file mode 100644 index 000000000000..f4b167830d9f --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/relay/+page.js @@ -0,0 +1,7 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const res = await fetch('/load/relay.json'); + return { + answer: await res.json() + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/relay/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/relay/+page.svelte new file mode 100644 index 000000000000..7c7268b87da6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/relay/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.answer}

diff --git a/packages/kit/test/apps/basics/src/routes/load/serialization-post.json.js b/packages/kit/test/apps/basics/src/routes/load/serialization-post.json/+server.js similarity index 76% rename from packages/kit/test/apps/basics/src/routes/load/serialization-post.json.js rename to packages/kit/test/apps/basics/src/routes/load/serialization-post.json/+server.js index 413111fc5594..5b71d6927309 100644 --- a/packages/kit/test/apps/basics/src/routes/load/serialization-post.json.js +++ b/packages/kit/test/apps/basics/src/routes/load/serialization-post.json/+server.js @@ -1,8 +1,5 @@ /** @type {import('@sveltejs/kit').RequestHandler} */ export async function POST({ request }) { const body = await request.text(); - - return { - body: body.toUpperCase() - }; + return new Response(body.toUpperCase()); } diff --git a/packages/kit/test/apps/basics/src/routes/load/serialization-post.svelte b/packages/kit/test/apps/basics/src/routes/load/serialization-post.svelte deleted file mode 100644 index e4162fffafd7..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/serialization-post.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - - - -

a: {a}

-

b: {b}

diff --git a/packages/kit/test/apps/basics/src/routes/load/serialization-post/+page.js b/packages/kit/test/apps/basics/src/routes/load/serialization-post/+page.js new file mode 100644 index 000000000000..d5d5704b7d0c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/serialization-post/+page.js @@ -0,0 +1,19 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + /** @param {string} body */ + async function post(body) { + const res = await fetch('/load/serialization-post.json', { + method: 'POST', + headers: { + 'content-type': 'text/plain' + }, + body + }); + + return await res.text(); + } + const a = await post('x'); + const b = await post('y'); + + return { a, b }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/serialization-post/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/serialization-post/+page.svelte new file mode 100644 index 000000000000..53e2ed09bee7 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/serialization-post/+page.svelte @@ -0,0 +1,7 @@ + + +

a: {data.a}

+

b: {data.b}

diff --git a/packages/kit/test/apps/basics/src/routes/load/serialization.json.js b/packages/kit/test/apps/basics/src/routes/load/serialization.json.js deleted file mode 100644 index 863b891d5c88..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/serialization.json.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - answer: 42 - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/load/serialization.json/+server.js b/packages/kit/test/apps/basics/src/routes/load/serialization.json/+server.js new file mode 100644 index 000000000000..0c61057c9dbd --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/serialization.json/+server.js @@ -0,0 +1,8 @@ +import { json } from '@sveltejs/kit'; + +/** @type {import('./$types').RequestHandler} */ +export function GET() { + return json({ + answer: 42 + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/load/serialization.svelte b/packages/kit/test/apps/basics/src/routes/load/serialization.svelte deleted file mode 100644 index 70c4284f5db6..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/serialization.svelte +++ /dev/null @@ -1,16 +0,0 @@ - - - - -

{answer}

diff --git a/packages/kit/test/apps/basics/src/routes/load/serialization/+page.js b/packages/kit/test/apps/basics/src/routes/load/serialization/+page.js new file mode 100644 index 000000000000..6f10d05a7a2c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/serialization/+page.js @@ -0,0 +1,5 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const res = await fetch('/load/serialization.json'); + return await res.json(); +} diff --git a/packages/kit/test/apps/basics/src/routes/load/serialization/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/serialization/+page.svelte new file mode 100644 index 000000000000..7c7268b87da6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/serialization/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.answer}

diff --git a/packages/kit/test/apps/basics/src/routes/load/server-fetch-request.svelte b/packages/kit/test/apps/basics/src/routes/load/server-fetch-request.svelte deleted file mode 100644 index c1f3661c5a56..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/server-fetch-request.svelte +++ /dev/null @@ -1,19 +0,0 @@ - - - - -

the answer is {answer}

diff --git a/packages/kit/test/apps/basics/src/routes/load/server-fetch-request/+page.js b/packages/kit/test/apps/basics/src/routes/load/server-fetch-request/+page.js new file mode 100644 index 000000000000..8ea14e87c91d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/server-fetch-request/+page.js @@ -0,0 +1,8 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ url, fetch }) { + const href = `http://localhost:${url.searchParams.get('port')}/server-fetch-request.json`; + + const res = await fetch(href); + const { answer } = await res.json(); + return { answer }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/server-fetch-request/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/server-fetch-request/+page.svelte new file mode 100644 index 000000000000..c890e7840e38 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/server-fetch-request/+page.svelte @@ -0,0 +1,6 @@ + + +

the answer is {data.answer}

diff --git a/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/+page.js b/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/+page.js new file mode 100644 index 000000000000..5807c2f5fb55 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/+page.js @@ -0,0 +1,7 @@ +export async function load({ fetch, url }) { + await fetch(`/load/set-cookie-fetch/a.json${url.search}`); + const res = await fetch('/load/set-cookie-fetch/b.json'); + const { answer } = await res.json(); // need to read the response so it gets serialized + + return { answer }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/+page.svelte new file mode 100644 index 000000000000..c890e7840e38 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/+page.svelte @@ -0,0 +1,6 @@ + + +

the answer is {data.answer}

diff --git a/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/a.json.js b/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/a.json.js deleted file mode 100644 index 8e6c7ffe5a14..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/a.json.js +++ /dev/null @@ -1,11 +0,0 @@ -/** @type {import('./__types/a.json').RequestHandler} */ -export function GET({ url }) { - const answer = url.searchParams.get('answer') || '42'; - - return { - headers: { - 'set-cookie': `answer=${answer}; HttpOnly; Path=/load/set-cookie-fetch` - }, - body: {} - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/a.json/+server.js b/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/a.json/+server.js new file mode 100644 index 000000000000..5350777d1078 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/a.json/+server.js @@ -0,0 +1,15 @@ +import { json } from '@sveltejs/kit'; + +/** @type {import('./$types').RequestHandler} */ +export function GET({ url }) { + const answer = url.searchParams.get('answer') || '42'; + + return json( + {}, + { + headers: { + 'set-cookie': `answer=${answer}; HttpOnly; Path=/load/set-cookie-fetch` + } + } + ); +} diff --git a/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/b.json.js b/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/b.json.js deleted file mode 100644 index 6c6bfb42417e..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/b.json.js +++ /dev/null @@ -1,16 +0,0 @@ -/** @type {import('./__types/b.json').RequestHandler} */ -export function GET({ request }) { - const cookie = request.headers.get('cookie'); - - const match = /answer=([^;]+)/.exec(cookie); - const answer = +match?.[1]; - - return { - body: { - answer - }, - headers: { - 'set-cookie': `doubled=${answer * 2}; HttpOnly; Path=/load/set-cookie-fetch` - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/b.json/+server.js b/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/b.json/+server.js new file mode 100644 index 000000000000..15c8f34f3714 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/b.json/+server.js @@ -0,0 +1,18 @@ +import { json } from '@sveltejs/kit'; + +/** @type {import('./$types').RequestHandler} */ +export function GET({ request }) { + const cookie = request.headers.get('cookie'); + + const match = /answer=([^;]+)/.exec(cookie); + const answer = +match?.[1]; + + return json( + { answer }, + { + headers: { + 'set-cookie': `doubled=${answer * 2}; HttpOnly; Path=/load/set-cookie-fetch` + } + } + ); +} diff --git a/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/index.svelte b/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/index.svelte deleted file mode 100644 index 78d7b82b9395..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/set-cookie-fetch/index.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - - -

the answer is {answer}

diff --git a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/[z].svelte b/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/[z].svelte deleted file mode 100644 index f5795357b64f..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/[z].svelte +++ /dev/null @@ -1,28 +0,0 @@ - - - - -

message: {message}

-
{JSON.stringify({ x, y, z })}
diff --git a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/__layout.svelte b/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/__layout.svelte deleted file mode 100644 index dfc6f03d8e2d..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/__layout.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/__layout.svelte b/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/__layout.svelte deleted file mode 100644 index 87cda94c6eb5..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/__layout.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/packages/kit/test/apps/basics/src/routes/load/stuff/__layout.svelte b/packages/kit/test/apps/basics/src/routes/load/stuff/__layout.svelte deleted file mode 100644 index 106a0f7479c4..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/stuff/__layout.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/packages/kit/test/apps/basics/src/routes/load/url-hash.svelte b/packages/kit/test/apps/basics/src/routes/load/url-hash.svelte deleted file mode 100644 index 86e1063af418..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/url-hash.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - - - -

- {hash} -

diff --git a/packages/kit/test/apps/basics/src/routes/load/url-hash/+page.js b/packages/kit/test/apps/basics/src/routes/load/url-hash/+page.js new file mode 100644 index 000000000000..1837c72c8826 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/url-hash/+page.js @@ -0,0 +1,4 @@ +export const load = ({ url }) => { + const hash = url.hash; + return { hash }; +}; diff --git a/packages/kit/test/apps/basics/src/routes/load/url-hash/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/url-hash/+page.svelte new file mode 100644 index 000000000000..46dae9e929d0 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/url-hash/+page.svelte @@ -0,0 +1,7 @@ + + +

+ {data.hash} +

diff --git a/packages/kit/test/apps/basics/src/routes/load/url-to-string.svelte b/packages/kit/test/apps/basics/src/routes/load/url-to-string.svelte deleted file mode 100644 index 122dad20b1a3..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/url-to-string.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - -

I didn't break!

diff --git a/packages/kit/test/apps/basics/src/routes/load/url-to-string/+page.js b/packages/kit/test/apps/basics/src/routes/load/url-to-string/+page.js new file mode 100644 index 000000000000..dce00d994d4e --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/url-to-string/+page.js @@ -0,0 +1,4 @@ +export const load = ({ url }) => { + url.toString(); + return {}; +}; diff --git a/packages/kit/test/apps/basics/src/routes/load/url-to-string/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/url-to-string/+page.svelte new file mode 100644 index 000000000000..24b0c12ddf1a --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/url-to-string/+page.svelte @@ -0,0 +1 @@ +

I didn't break!

diff --git a/packages/kit/test/apps/basics/src/routes/load/window-fetch/correct.svelte b/packages/kit/test/apps/basics/src/routes/load/window-fetch/correct.svelte deleted file mode 100644 index dcb7afbc89dd..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/window-fetch/correct.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - - -

{answer}

diff --git a/packages/kit/test/apps/basics/src/routes/load/window-fetch/correct/+page.js b/packages/kit/test/apps/basics/src/routes/load/window-fetch/correct/+page.js new file mode 100644 index 000000000000..29f4a034374d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/window-fetch/correct/+page.js @@ -0,0 +1,6 @@ +/** @type {import('./$types').PageLoad} */ +export async function load({ url, fetch }) { + const res = await fetch(`${url.origin}/load/window-fetch/data.json`); + const { answer } = await res.json(); + return { answer }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/window-fetch/correct/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/window-fetch/correct/+page.svelte new file mode 100644 index 000000000000..7c7268b87da6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/window-fetch/correct/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.answer}

diff --git a/packages/kit/test/apps/basics/src/routes/load/window-fetch/data.json.js b/packages/kit/test/apps/basics/src/routes/load/window-fetch/data.json.js deleted file mode 100644 index 5af3eaf447d0..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/window-fetch/data.json.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('./__types/data.json').RequestHandler} */ -export function GET() { - return { - body: { - answer: 42 - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/load/window-fetch/data.json/+server.js b/packages/kit/test/apps/basics/src/routes/load/window-fetch/data.json/+server.js new file mode 100644 index 000000000000..0c61057c9dbd --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/window-fetch/data.json/+server.js @@ -0,0 +1,8 @@ +import { json } from '@sveltejs/kit'; + +/** @type {import('./$types').RequestHandler} */ +export function GET() { + return json({ + answer: 42 + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/load/window-fetch/incorrect.svelte b/packages/kit/test/apps/basics/src/routes/load/window-fetch/incorrect.svelte deleted file mode 100644 index f669c7c33470..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/window-fetch/incorrect.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - - -

{answer}

diff --git a/packages/kit/test/apps/basics/src/routes/load/window-fetch/incorrect/+page.js b/packages/kit/test/apps/basics/src/routes/load/window-fetch/incorrect/+page.js new file mode 100644 index 000000000000..a7b4b250838b --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/window-fetch/incorrect/+page.js @@ -0,0 +1,6 @@ +/** @type {import('./$types').PageLoad} */ +export async function load({ url }) { + const res = await fetch(`${url.origin}/load/window-fetch/data.json`); + const { answer } = await res.json(); + return { answer }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/window-fetch/incorrect/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/window-fetch/incorrect/+page.svelte new file mode 100644 index 000000000000..7c7268b87da6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/window-fetch/incorrect/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.answer}

diff --git a/packages/kit/test/apps/basics/src/routes/method-override/+page.js b/packages/kit/test/apps/basics/src/routes/method-override/+page.js new file mode 100644 index 000000000000..cdf44934798d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/method-override/+page.js @@ -0,0 +1,6 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ url }) { + return { + method: url.searchParams.get('method') || '' + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/method-override/index.svelte b/packages/kit/test/apps/basics/src/routes/method-override/+page.svelte similarity index 73% rename from packages/kit/test/apps/basics/src/routes/method-override/index.svelte rename to packages/kit/test/apps/basics/src/routes/method-override/+page.svelte index f88ae1417ef9..0e307c2dd328 100644 --- a/packages/kit/test/apps/basics/src/routes/method-override/index.svelte +++ b/packages/kit/test/apps/basics/src/routes/method-override/+page.svelte @@ -1,20 +1,9 @@ - - -

{method}

+

{data.method}

diff --git a/packages/kit/test/apps/basics/src/routes/method-override/fetch.json.js b/packages/kit/test/apps/basics/src/routes/method-override/fetch.json/+server.js similarity index 76% rename from packages/kit/test/apps/basics/src/routes/method-override/fetch.json.js rename to packages/kit/test/apps/basics/src/routes/method-override/fetch.json/+server.js index 58616bf772a1..7f4b4c8c37b5 100644 --- a/packages/kit/test/apps/basics/src/routes/method-override/fetch.json.js +++ b/packages/kit/test/apps/basics/src/routes/method-override/fetch.json/+server.js @@ -1,9 +1,10 @@ -const buildResponse = (/** @type {string} */ method) => ({ - status: 303, - headers: { - location: `/method-override?method=${method}` - } -}); +const buildResponse = (/** @type {string} */ method) => + new Response(undefined, { + status: 303, + headers: { + location: `/method-override?method=${method}` + } + }); /** @type {import('@sveltejs/kit').RequestHandler} */ export const GET = ({ request }) => { diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/__layout.svelte b/packages/kit/test/apps/basics/src/routes/nested-layout/+layout.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/nested-layout/__layout.svelte rename to packages/kit/test/apps/basics/src/routes/nested-layout/+layout.svelte diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/index.svelte b/packages/kit/test/apps/basics/src/routes/nested-layout/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/nested-layout/index.svelte rename to packages/kit/test/apps/basics/src/routes/nested-layout/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/error.svelte b/packages/kit/test/apps/basics/src/routes/nested-layout/error.svelte deleted file mode 100644 index 42624b81fe8c..000000000000 --- a/packages/kit/test/apps/basics/src/routes/nested-layout/error.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -

Not shown

diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/error/+page.js b/packages/kit/test/apps/basics/src/routes/nested-layout/error/+page.js new file mode 100644 index 000000000000..9b17193d01e2 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/nested-layout/error/+page.js @@ -0,0 +1,5 @@ +import { error } from '@sveltejs/kit'; + +export function load() { + throw error(500, 'Error'); +} diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/error/+page.svelte b/packages/kit/test/apps/basics/src/routes/nested-layout/error/+page.svelte new file mode 100644 index 000000000000..ec39d8d206b0 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/nested-layout/error/+page.svelte @@ -0,0 +1 @@ +

Not shown

diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/foo/__layout.svelte b/packages/kit/test/apps/basics/src/routes/nested-layout/foo/+layout.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/nested-layout/foo/__layout.svelte rename to packages/kit/test/apps/basics/src/routes/nested-layout/foo/+layout.svelte diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/+error.svelte b/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/+error.svelte new file mode 100644 index 000000000000..5301c04c4566 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/+error.svelte @@ -0,0 +1,13 @@ + + +

Nested error page

+

status: {$page.status}

+

error.message: {$page.error.message}

+ + diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/__layout.svelte b/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/+layout.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/__layout.svelte rename to packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/+layout.svelte diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/__error.svelte b/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/__error.svelte deleted file mode 100644 index cf9f99bd10c2..000000000000 --- a/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/__error.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - - - -

Nested error page

-

status: {status}

-

error.message: {error && error.message}

-

answer: {answer}

- - diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/nope.svelte b/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/nope.svelte deleted file mode 100644 index 8c852515d266..000000000000 --- a/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/nope.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -

should not see this

diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/nope/+page.js b/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/nope/+page.js new file mode 100644 index 000000000000..ba6bb3928f9f --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/nope/+page.js @@ -0,0 +1,3 @@ +export async function load() { + throw new Error('nope'); +} diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/nope/+page.svelte b/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/nope/+page.svelte new file mode 100644 index 000000000000..c9ce7c1c8743 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/nope/+page.svelte @@ -0,0 +1 @@ +

should not see this

diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/foo/baz.svelte b/packages/kit/test/apps/basics/src/routes/nested-layout/foo/baz/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/nested-layout/foo/baz.svelte rename to packages/kit/test/apps/basics/src/routes/nested-layout/foo/baz/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/reset/__layout@blank.svelte b/packages/kit/test/apps/basics/src/routes/nested-layout/reset/+layout@blank.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/nested-layout/reset/__layout@blank.svelte rename to packages/kit/test/apps/basics/src/routes/nested-layout/reset/+layout@blank.svelte diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/reset/index.svelte b/packages/kit/test/apps/basics/src/routes/nested-layout/reset/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/nested-layout/reset/index.svelte rename to packages/kit/test/apps/basics/src/routes/nested-layout/reset/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/no-hydrate.json/+server.js b/packages/kit/test/apps/basics/src/routes/no-hydrate.json/+server.js new file mode 100644 index 000000000000..0fabcc3f4ad6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/no-hydrate.json/+server.js @@ -0,0 +1,7 @@ +import { json } from '@sveltejs/kit'; + +export function GET() { + return json({ + type: 'no-hydrate' + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/no-hydrate/+page.js b/packages/kit/test/apps/basics/src/routes/no-hydrate/+page.js new file mode 100644 index 000000000000..eddde8cf1a50 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/no-hydrate/+page.js @@ -0,0 +1,11 @@ +export const hydrate = false; + +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const res = await fetch('/no-hydrate.json'); + + /** @type {any} */ + const { type } = await res.json(); + + return { type }; +} diff --git a/packages/kit/test/apps/basics/src/routes/no-hydrate/+page.svelte b/packages/kit/test/apps/basics/src/routes/no-hydrate/+page.svelte new file mode 100644 index 000000000000..6ca14342e1ea --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/no-hydrate/+page.svelte @@ -0,0 +1,12 @@ + + + + +other diff --git a/packages/kit/test/apps/basics/src/routes/no-hydrate/index.json.js b/packages/kit/test/apps/basics/src/routes/no-hydrate/index.json.js deleted file mode 100644 index 6a1e3ec4422a..000000000000 --- a/packages/kit/test/apps/basics/src/routes/no-hydrate/index.json.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - type: 'no-hydrate' - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/no-hydrate/index.svelte b/packages/kit/test/apps/basics/src/routes/no-hydrate/index.svelte deleted file mode 100644 index 966b9c417a35..000000000000 --- a/packages/kit/test/apps/basics/src/routes/no-hydrate/index.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - -other diff --git a/packages/kit/test/apps/basics/src/routes/no-hydrate/no-js.svelte b/packages/kit/test/apps/basics/src/routes/no-hydrate/no-js.svelte deleted file mode 100644 index 00395f4a0d65..000000000000 --- a/packages/kit/test/apps/basics/src/routes/no-hydrate/no-js.svelte +++ /dev/null @@ -1,6 +0,0 @@ - - -

look ma no javascript

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/no-hydrate/no-js/+page.js b/packages/kit/test/apps/basics/src/routes/no-hydrate/no-js/+page.js new file mode 100644 index 000000000000..f043bbaa8e4a --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/no-hydrate/no-js/+page.js @@ -0,0 +1,2 @@ +export const router = false; +export const hydrate = false; diff --git a/packages/kit/test/apps/basics/src/routes/no-hydrate/no-js/+page.svelte b/packages/kit/test/apps/basics/src/routes/no-hydrate/no-js/+page.svelte new file mode 100644 index 000000000000..a840a265dd12 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/no-hydrate/no-js/+page.svelte @@ -0,0 +1 @@ +

look ma no javascript

diff --git a/packages/kit/test/apps/basics/src/routes/no-hydrate/other.svelte b/packages/kit/test/apps/basics/src/routes/no-hydrate/other.svelte deleted file mode 100644 index 7650a6aa7cab..000000000000 --- a/packages/kit/test/apps/basics/src/routes/no-hydrate/other.svelte +++ /dev/null @@ -1 +0,0 @@ -hydrate \ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/no-hydrate/other/+page.svelte b/packages/kit/test/apps/basics/src/routes/no-hydrate/other/+page.svelte new file mode 100644 index 000000000000..2ac0e4037772 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/no-hydrate/other/+page.svelte @@ -0,0 +1 @@ +hydrate diff --git a/packages/kit/test/apps/basics/src/routes/no-router/__layout.svelte b/packages/kit/test/apps/basics/src/routes/no-router/+layout.svelte similarity index 91% rename from packages/kit/test/apps/basics/src/routes/no-router/__layout.svelte rename to packages/kit/test/apps/basics/src/routes/no-router/+layout.svelte index 918af78c5965..ef4b0dbfea12 100644 --- a/packages/kit/test/apps/basics/src/routes/no-router/__layout.svelte +++ b/packages/kit/test/apps/basics/src/routes/no-router/+layout.svelte @@ -9,4 +9,4 @@ a b - \ No newline at end of file + diff --git a/packages/kit/test/apps/basics/src/routes/no-router/a.svelte b/packages/kit/test/apps/basics/src/routes/no-router/a.svelte deleted file mode 100644 index 32bd0fc793a0..000000000000 --- a/packages/kit/test/apps/basics/src/routes/no-router/a.svelte +++ /dev/null @@ -1,5 +0,0 @@ - - -

a

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/no-router/a/+page.js b/packages/kit/test/apps/basics/src/routes/no-router/a/+page.js new file mode 100644 index 000000000000..90f739775e8b --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/no-router/a/+page.js @@ -0,0 +1 @@ +export const router = false; diff --git a/packages/kit/test/apps/basics/src/routes/no-router/a/+page.svelte b/packages/kit/test/apps/basics/src/routes/no-router/a/+page.svelte new file mode 100644 index 000000000000..57f9d40797b8 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/no-router/a/+page.svelte @@ -0,0 +1 @@ +

a

diff --git a/packages/kit/test/apps/basics/src/routes/no-router/b/+page.svelte b/packages/kit/test/apps/basics/src/routes/no-router/b/+page.svelte new file mode 100644 index 000000000000..e96ed3db8987 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/no-router/b/+page.svelte @@ -0,0 +1 @@ +

b

diff --git a/packages/kit/test/apps/basics/src/routes/no-ssr/index.svelte b/packages/kit/test/apps/basics/src/routes/no-ssr/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/no-ssr/index.svelte rename to packages/kit/test/apps/basics/src/routes/no-ssr/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/no-ssr/margin.svelte b/packages/kit/test/apps/basics/src/routes/no-ssr/margin/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/no-ssr/margin.svelte rename to packages/kit/test/apps/basics/src/routes/no-ssr/margin/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/no-ssr/other.svelte b/packages/kit/test/apps/basics/src/routes/no-ssr/other/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/no-ssr/other.svelte rename to packages/kit/test/apps/basics/src/routes/no-ssr/other/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/origin/index.svelte b/packages/kit/test/apps/basics/src/routes/origin/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/origin/index.svelte rename to packages/kit/test/apps/basics/src/routes/origin/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/package.json.js b/packages/kit/test/apps/basics/src/routes/package.json.js deleted file mode 100644 index 9f90e4e60bfa..000000000000 --- a/packages/kit/test/apps/basics/src/routes/package.json.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - works: true - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/package.json/+server.js b/packages/kit/test/apps/basics/src/routes/package.json/+server.js new file mode 100644 index 000000000000..034fb178c736 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/package.json/+server.js @@ -0,0 +1,7 @@ +import { json } from '@sveltejs/kit'; + +export function GET() { + return json({ + works: true + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/paths/index.svelte b/packages/kit/test/apps/basics/src/routes/paths/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/paths/index.svelte rename to packages/kit/test/apps/basics/src/routes/paths/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/prerendering/+error.svelte b/packages/kit/test/apps/basics/src/routes/prerendering/+error.svelte new file mode 100644 index 000000000000..147fd7255b3b --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/prerendering/+error.svelte @@ -0,0 +1,7 @@ + + +

{title}

diff --git a/packages/kit/test/apps/basics/src/routes/prerendering/+page.js b/packages/kit/test/apps/basics/src/routes/prerendering/+page.js new file mode 100644 index 000000000000..5816f68fb244 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/prerendering/+page.js @@ -0,0 +1,5 @@ +export const prerender = true; + +export const load = ({ session }) => { + return session; +}; diff --git a/packages/kit/test/apps/basics/src/routes/prerendering/+page.svelte b/packages/kit/test/apps/basics/src/routes/prerendering/+page.svelte new file mode 100644 index 000000000000..ce23336eadef --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/prerendering/+page.svelte @@ -0,0 +1 @@ +

You should never see me.

diff --git a/packages/kit/test/apps/basics/src/routes/prerendering/__error.svelte b/packages/kit/test/apps/basics/src/routes/prerendering/__error.svelte deleted file mode 100644 index 50113b8d0cd4..000000000000 --- a/packages/kit/test/apps/basics/src/routes/prerendering/__error.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - - - -

{title}

diff --git a/packages/kit/test/apps/basics/src/routes/prerendering/index.svelte b/packages/kit/test/apps/basics/src/routes/prerendering/index.svelte deleted file mode 100644 index 2746e9670173..000000000000 --- a/packages/kit/test/apps/basics/src/routes/prerendering/index.svelte +++ /dev/null @@ -1,11 +0,0 @@ - - -

You should never see me.

diff --git a/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint.svelte b/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint.svelte deleted file mode 100644 index 17e79b8eb97b..000000000000 --- a/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint.svelte +++ /dev/null @@ -1,5 +0,0 @@ - - -

I have a mutative endpoint!

diff --git a/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint/+page.js b/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint/+page.js new file mode 100644 index 000000000000..189f71e2e1b3 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint/+page.js @@ -0,0 +1 @@ +export const prerender = true; diff --git a/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint.ts b/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint/+page.server.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint.ts rename to packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint/+page.server.ts diff --git a/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint/+page.svelte b/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint/+page.svelte new file mode 100644 index 000000000000..d063341bea8c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint/+page.svelte @@ -0,0 +1 @@ +

I have a mutative endpoint!

diff --git a/packages/kit/test/apps/basics/src/routes/query/echo.svelte b/packages/kit/test/apps/basics/src/routes/query/echo.svelte deleted file mode 100644 index fa96a56a43c3..000000000000 --- a/packages/kit/test/apps/basics/src/routes/query/echo.svelte +++ /dev/null @@ -1,37 +0,0 @@ - - - - -
{JSON.stringify(values)}
-
{JSON.stringify(to_pojo($page.url.searchParams))}
- -?bar=2 diff --git a/packages/kit/test/apps/basics/src/routes/query/echo/+page.js b/packages/kit/test/apps/basics/src/routes/query/echo/+page.js new file mode 100644 index 000000000000..49fb18bd3229 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/query/echo/+page.js @@ -0,0 +1,8 @@ +import { to_pojo } from './utils.js'; + +/** @type {import('@sveltejs/kit').Load} */ +export function load({ url }) { + return { + values: to_pojo(url.searchParams) + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/query/echo/+page.svelte b/packages/kit/test/apps/basics/src/routes/query/echo/+page.svelte new file mode 100644 index 000000000000..2788779b22c4 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/query/echo/+page.svelte @@ -0,0 +1,12 @@ + + +
{JSON.stringify(data.values)}
+
{JSON.stringify(to_pojo($page.url.searchParams))}
+ +?bar=2 diff --git a/packages/kit/test/apps/basics/src/routes/query/echo/utils.js b/packages/kit/test/apps/basics/src/routes/query/echo/utils.js new file mode 100644 index 000000000000..97023e74857f --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/query/echo/utils.js @@ -0,0 +1,14 @@ +/** @typedef {Record} Query */ + +/** @param {URLSearchParams} query */ +export function to_pojo(query) { + /** @type {Query}*/ + const values = {}; + + query.forEach((value, key) => { + if (!(key in values)) values[key] = []; + values[key].push(value); + }); + + return values; +} diff --git a/packages/kit/test/apps/basics/src/routes/redirect-on-load/+page.js b/packages/kit/test/apps/basics/src/routes/redirect-on-load/+page.js new file mode 100644 index 000000000000..5cf9a8a68148 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/redirect-on-load/+page.js @@ -0,0 +1,10 @@ +import { redirect } from '@sveltejs/kit'; +import { browser } from '$app/env'; + +export async function load() { + if (browser) { + throw redirect(303, '/redirect-on-load/redirected'); + } + + return {}; +} diff --git a/packages/kit/test/apps/basics/src/routes/redirect-on-load/+page.svelte b/packages/kit/test/apps/basics/src/routes/redirect-on-load/+page.svelte new file mode 100644 index 000000000000..9b01e2e1fd28 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/redirect-on-load/+page.svelte @@ -0,0 +1,2 @@ +

Woops!

+

You shouldn't be here. You should have been directed to /redirect!

diff --git a/packages/kit/test/apps/basics/src/routes/redirect-on-load/index.svelte b/packages/kit/test/apps/basics/src/routes/redirect-on-load/index.svelte deleted file mode 100644 index 7a25daffdf11..000000000000 --- a/packages/kit/test/apps/basics/src/routes/redirect-on-load/index.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - -

Woops!

-

You shouldn't be here. You should have been directed to /redirect!

diff --git a/packages/kit/test/apps/basics/src/routes/redirect-on-load/redirected.svelte b/packages/kit/test/apps/basics/src/routes/redirect-on-load/redirected/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/redirect-on-load/redirected.svelte rename to packages/kit/test/apps/basics/src/routes/redirect-on-load/redirected/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/redirect/index.svelte b/packages/kit/test/apps/basics/src/routes/redirect/+page.svelte similarity index 78% rename from packages/kit/test/apps/basics/src/routes/redirect/index.svelte rename to packages/kit/test/apps/basics/src/routes/redirect/+page.svelte index 856e73216a4b..70f0a624f0ff 100644 --- a/packages/kit/test/apps/basics/src/routes/redirect/index.svelte +++ b/packages/kit/test/apps/basics/src/routes/redirect/+page.svelte @@ -6,4 +6,4 @@ b (loopy) a (missing-status) -b (missing-status) \ No newline at end of file +b (missing-status) diff --git a/packages/kit/test/apps/basics/src/routes/redirect/a.svelte b/packages/kit/test/apps/basics/src/routes/redirect/a.svelte deleted file mode 100644 index 40af92be8e54..000000000000 --- a/packages/kit/test/apps/basics/src/routes/redirect/a.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -

a

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/redirect/a/+page.js b/packages/kit/test/apps/basics/src/routes/redirect/a/+page.js new file mode 100644 index 000000000000..93aaf22b311b --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/redirect/a/+page.js @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export function load() { + throw redirect(307, './b'); +} diff --git a/packages/kit/test/apps/basics/src/routes/redirect/a/+page.svelte b/packages/kit/test/apps/basics/src/routes/redirect/a/+page.svelte new file mode 100644 index 000000000000..57f9d40797b8 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/redirect/a/+page.svelte @@ -0,0 +1 @@ +

a

diff --git a/packages/kit/test/apps/basics/src/routes/redirect/b.svelte b/packages/kit/test/apps/basics/src/routes/redirect/b.svelte deleted file mode 100644 index 09f9f7f76961..000000000000 --- a/packages/kit/test/apps/basics/src/routes/redirect/b.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -

a

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/redirect/b/+page.js b/packages/kit/test/apps/basics/src/routes/redirect/b/+page.js new file mode 100644 index 000000000000..169eb727728a --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/redirect/b/+page.js @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export function load() { + throw redirect(307, './c'); +} diff --git a/packages/kit/test/apps/basics/src/routes/redirect/b/+page.svelte b/packages/kit/test/apps/basics/src/routes/redirect/b/+page.svelte new file mode 100644 index 000000000000..57f9d40797b8 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/redirect/b/+page.svelte @@ -0,0 +1 @@ +

a

diff --git a/packages/kit/test/apps/basics/src/routes/redirect/c.svelte b/packages/kit/test/apps/basics/src/routes/redirect/c/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/redirect/c.svelte rename to packages/kit/test/apps/basics/src/routes/redirect/c/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/redirect/loopy/a.svelte b/packages/kit/test/apps/basics/src/routes/redirect/loopy/a.svelte deleted file mode 100644 index 40af92be8e54..000000000000 --- a/packages/kit/test/apps/basics/src/routes/redirect/loopy/a.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -

a

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/redirect/loopy/a/+page.js b/packages/kit/test/apps/basics/src/routes/redirect/loopy/a/+page.js new file mode 100644 index 000000000000..93aaf22b311b --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/redirect/loopy/a/+page.js @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export function load() { + throw redirect(307, './b'); +} diff --git a/packages/kit/test/apps/basics/src/routes/redirect/loopy/a/+page.svelte b/packages/kit/test/apps/basics/src/routes/redirect/loopy/a/+page.svelte new file mode 100644 index 000000000000..57f9d40797b8 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/redirect/loopy/a/+page.svelte @@ -0,0 +1 @@ +

a

diff --git a/packages/kit/test/apps/basics/src/routes/redirect/loopy/b.svelte b/packages/kit/test/apps/basics/src/routes/redirect/loopy/b.svelte deleted file mode 100644 index 97fd91c1b9b3..000000000000 --- a/packages/kit/test/apps/basics/src/routes/redirect/loopy/b.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -

b

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/redirect/loopy/b/+page.js b/packages/kit/test/apps/basics/src/routes/redirect/loopy/b/+page.js new file mode 100644 index 000000000000..98098bf9a4ea --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/redirect/loopy/b/+page.js @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export function load() { + throw redirect(307, './a'); +} diff --git a/packages/kit/test/apps/basics/src/routes/redirect/loopy/b/+page.svelte b/packages/kit/test/apps/basics/src/routes/redirect/loopy/b/+page.svelte new file mode 100644 index 000000000000..e96ed3db8987 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/redirect/loopy/b/+page.svelte @@ -0,0 +1 @@ +

b

diff --git a/packages/kit/test/apps/basics/src/routes/redirect/missing-status/a.svelte b/packages/kit/test/apps/basics/src/routes/redirect/missing-status/a.svelte deleted file mode 100644 index d4706643643b..000000000000 --- a/packages/kit/test/apps/basics/src/routes/redirect/missing-status/a.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - -

a

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/redirect/missing-status/a/+page.js b/packages/kit/test/apps/basics/src/routes/redirect/missing-status/a/+page.js new file mode 100644 index 000000000000..42f945943124 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/redirect/missing-status/a/+page.js @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export function load() { + throw redirect(undefined, './b'); +} diff --git a/packages/kit/test/apps/basics/src/routes/redirect/missing-status/a/+page.svelte b/packages/kit/test/apps/basics/src/routes/redirect/missing-status/a/+page.svelte new file mode 100644 index 000000000000..57f9d40797b8 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/redirect/missing-status/a/+page.svelte @@ -0,0 +1 @@ +

a

diff --git a/packages/kit/test/apps/basics/src/routes/redirect/missing-status/b.svelte b/packages/kit/test/apps/basics/src/routes/redirect/missing-status/b.svelte deleted file mode 100644 index cd420e238cd6..000000000000 --- a/packages/kit/test/apps/basics/src/routes/redirect/missing-status/b.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -

b

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/redirect/missing-status/b/+page.js b/packages/kit/test/apps/basics/src/routes/redirect/missing-status/b/+page.js new file mode 100644 index 000000000000..5aba1d550606 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/redirect/missing-status/b/+page.js @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export function load() { + throw redirect(555, './a'); +} diff --git a/packages/kit/test/apps/basics/src/routes/redirect/missing-status/b/+page.svelte b/packages/kit/test/apps/basics/src/routes/redirect/missing-status/b/+page.svelte new file mode 100644 index 000000000000..e96ed3db8987 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/redirect/missing-status/b/+page.svelte @@ -0,0 +1 @@ +

b

diff --git a/packages/kit/test/apps/basics/src/routes/routing/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/index.svelte rename to packages/kit/test/apps/basics/src/routes/routing/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/[slug].svelte b/packages/kit/test/apps/basics/src/routes/routing/[slug]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/[slug].svelte rename to packages/kit/test/apps/basics/src/routes/routing/[slug]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/a.svelte b/packages/kit/test/apps/basics/src/routes/routing/a/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/a.svelte rename to packages/kit/test/apps/basics/src/routes/routing/a/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/ambiguous/[slug].json.js b/packages/kit/test/apps/basics/src/routes/routing/ambiguous/[slug].json/+server.js similarity index 68% rename from packages/kit/test/apps/basics/src/routes/routing/ambiguous/[slug].json.js rename to packages/kit/test/apps/basics/src/routes/routing/ambiguous/[slug].json/+server.js index 640ba0ed3bc9..5dbfc114125c 100644 --- a/packages/kit/test/apps/basics/src/routes/routing/ambiguous/[slug].json.js +++ b/packages/kit/test/apps/basics/src/routes/routing/ambiguous/[slug].json/+server.js @@ -1,4 +1,4 @@ /** @type {import('@sveltejs/kit').RequestHandler} */ export function GET(req) { - return { body: req.params.slug }; + return new Response(req.params.slug); } diff --git a/packages/kit/test/apps/basics/src/routes/routing/ambiguous/[slug].svelte b/packages/kit/test/apps/basics/src/routes/routing/ambiguous/[slug].svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[wildcard].svelte b/packages/kit/test/apps/basics/src/routes/routing/ambiguous/[slug]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[wildcard].svelte rename to packages/kit/test/apps/basics/src/routes/routing/ambiguous/[slug]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/b.json/+server.js b/packages/kit/test/apps/basics/src/routes/routing/b.json/+server.js new file mode 100644 index 000000000000..9b173a271d27 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/b.json/+server.js @@ -0,0 +1,5 @@ +import { json } from '@sveltejs/kit'; + +export function GET() { + return json('b'); +} diff --git a/packages/kit/test/apps/basics/src/routes/routing/b/+page.js b/packages/kit/test/apps/basics/src/routes/routing/b/+page.js new file mode 100644 index 000000000000..695fb251b466 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/b/+page.js @@ -0,0 +1,5 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const letter = await fetch('/routing/b.json').then((r) => r.json()); + return { letter }; +} diff --git a/packages/kit/test/apps/basics/src/routes/routing/b/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/b/+page.svelte new file mode 100644 index 000000000000..ac25a891e5fd --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/b/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.letter}

diff --git a/packages/kit/test/apps/basics/src/routes/routing/b/index.json.js b/packages/kit/test/apps/basics/src/routes/routing/b/index.json.js deleted file mode 100644 index efb20b1b448a..000000000000 --- a/packages/kit/test/apps/basics/src/routes/routing/b/index.json.js +++ /dev/null @@ -1,3 +0,0 @@ -export function GET() { - return { body: JSON.stringify('b') }; -} diff --git a/packages/kit/test/apps/basics/src/routes/routing/b/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/b/index.svelte deleted file mode 100644 index ff012dd1c220..000000000000 --- a/packages/kit/test/apps/basics/src/routes/routing/b/index.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - - - -

{letter}

diff --git a/packages/kit/test/apps/basics/src/routes/routing/cancellation/__layout.svelte b/packages/kit/test/apps/basics/src/routes/routing/cancellation/+layout.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/cancellation/__layout.svelte rename to packages/kit/test/apps/basics/src/routes/routing/cancellation/+layout.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/cancellation/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/cancellation/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/cancellation/index.svelte rename to packages/kit/test/apps/basics/src/routes/routing/cancellation/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/cancellation/a.svelte b/packages/kit/test/apps/basics/src/routes/routing/cancellation/a.svelte deleted file mode 100644 index ec4ec8478ebb..000000000000 --- a/packages/kit/test/apps/basics/src/routes/routing/cancellation/a.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - -

this should not appear

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/routing/cancellation/a/+page.js b/packages/kit/test/apps/basics/src/routes/routing/cancellation/a/+page.js new file mode 100644 index 000000000000..a5091c42333e --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/cancellation/a/+page.js @@ -0,0 +1,12 @@ +import { browser } from '$app/env'; + +/** @type {import('@sveltejs/kit').Load} */ +export async function load() { + if (browser) { + await new Promise((f) => { + window.fulfil_navigation = f; + }); + } + + return {}; +} diff --git a/packages/kit/test/apps/basics/src/routes/routing/cancellation/a/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/cancellation/a/+page.svelte new file mode 100644 index 000000000000..66eeb86ad49c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/cancellation/a/+page.svelte @@ -0,0 +1 @@ +

this should not appear

diff --git a/packages/kit/test/apps/basics/src/routes/no-router/b.svelte b/packages/kit/test/apps/basics/src/routes/routing/cancellation/b/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/no-router/b.svelte rename to packages/kit/test/apps/basics/src/routes/routing/cancellation/b/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/client/bar/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/client/bar/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/client/bar/index.svelte rename to packages/kit/test/apps/basics/src/routes/routing/client/bar/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/client/bar/b.svelte b/packages/kit/test/apps/basics/src/routes/routing/client/bar/b.svelte deleted file mode 100644 index 51902b4da7a6..000000000000 --- a/packages/kit/test/apps/basics/src/routes/routing/client/bar/b.svelte +++ /dev/null @@ -1 +0,0 @@ -

b

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/routing/cancellation/b.svelte b/packages/kit/test/apps/basics/src/routes/routing/client/bar/b/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/cancellation/b.svelte rename to packages/kit/test/apps/basics/src/routes/routing/client/bar/b/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/client/foo.svelte b/packages/kit/test/apps/basics/src/routes/routing/client/foo/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/client/foo.svelte rename to packages/kit/test/apps/basics/src/routes/routing/client/foo/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/const.svelte b/packages/kit/test/apps/basics/src/routes/routing/const/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/const.svelte rename to packages/kit/test/apps/basics/src/routes/routing/const/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/dirs/bar/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/dirs/bar/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/dirs/bar/index.svelte rename to packages/kit/test/apps/basics/src/routes/routing/dirs/bar/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/dirs/bar/[a].svelte b/packages/kit/test/apps/basics/src/routes/routing/dirs/bar/[a]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/dirs/bar/[a].svelte rename to packages/kit/test/apps/basics/src/routes/routing/dirs/bar/[a]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/dirs/foo/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/dirs/foo/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/dirs/foo/index.svelte rename to packages/kit/test/apps/basics/src/routes/routing/dirs/foo/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/dirs/foo/[b].svelte b/packages/kit/test/apps/basics/src/routes/routing/dirs/foo/[b]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/dirs/foo/[b].svelte rename to packages/kit/test/apps/basics/src/routes/routing/dirs/foo/[b]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/hashes/a.svelte b/packages/kit/test/apps/basics/src/routes/routing/hashes/a/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/hashes/a.svelte rename to packages/kit/test/apps/basics/src/routes/routing/hashes/a/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/hashes/b.svelte b/packages/kit/test/apps/basics/src/routes/routing/hashes/b/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/hashes/b.svelte rename to packages/kit/test/apps/basics/src/routes/routing/hashes/b/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/hashes/pagestore.svelte b/packages/kit/test/apps/basics/src/routes/routing/hashes/pagestore/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/hashes/pagestore.svelte rename to packages/kit/test/apps/basics/src/routes/routing/hashes/pagestore/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/hashes/target.svelte b/packages/kit/test/apps/basics/src/routes/routing/hashes/target/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/hashes/target.svelte rename to packages/kit/test/apps/basics/src/routes/routing/hashes/target/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/matched/__layout.svelte b/packages/kit/test/apps/basics/src/routes/routing/matched/+layout.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/matched/__layout.svelte rename to packages/kit/test/apps/basics/src/routes/routing/matched/+layout.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/matched/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/matched/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/matched/index.svelte rename to packages/kit/test/apps/basics/src/routes/routing/matched/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/matched/[fallback].svelte b/packages/kit/test/apps/basics/src/routes/routing/matched/[fallback]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/matched/[fallback].svelte rename to packages/kit/test/apps/basics/src/routes/routing/matched/[fallback]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/matched/[letter=lowercase].svelte b/packages/kit/test/apps/basics/src/routes/routing/matched/[letter=lowercase]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/matched/[letter=lowercase].svelte rename to packages/kit/test/apps/basics/src/routes/routing/matched/[letter=lowercase]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/matched/[letter=uppercase].svelte b/packages/kit/test/apps/basics/src/routes/routing/matched/[letter=uppercase]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/matched/[letter=uppercase].svelte rename to packages/kit/test/apps/basics/src/routes/routing/matched/[letter=uppercase]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/matched/[number=numeric].svelte b/packages/kit/test/apps/basics/src/routes/routing/matched/[number=numeric]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/matched/[number=numeric].svelte rename to packages/kit/test/apps/basics/src/routes/routing/matched/[number=numeric]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/params-in-handle/[x].js b/packages/kit/test/apps/basics/src/routes/routing/params-in-handle/[x].js deleted file mode 100644 index 44e3c4ede219..000000000000 --- a/packages/kit/test/apps/basics/src/routes/routing/params-in-handle/[x].js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import('./__types/[x]').RequestHandler} */ -export function GET({ locals }) { - return { - body: { - key: locals.key, - params: locals.params - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/routing/params-in-handle/[x]/+server.js b/packages/kit/test/apps/basics/src/routes/routing/params-in-handle/[x]/+server.js new file mode 100644 index 000000000000..e02f13eba8d6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/params-in-handle/[x]/+server.js @@ -0,0 +1,9 @@ +import { json } from '@sveltejs/kit'; + +/** @type {import('./$types').RequestHandler} */ +export function GET({ locals }) { + return json({ + key: locals.key, + params: locals.params + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetched.json/+server.js b/packages/kit/test/apps/basics/src/routes/routing/prefetched.json/+server.js new file mode 100644 index 000000000000..04004e8da206 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/prefetched.json/+server.js @@ -0,0 +1,5 @@ +import { json } from '@sveltejs/kit'; + +export function GET() { + return json('prefetched'); +} diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetched/+page.js b/packages/kit/test/apps/basics/src/routes/routing/prefetched/+page.js new file mode 100644 index 000000000000..76a9f0c1cada --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/prefetched/+page.js @@ -0,0 +1,5 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const message = await fetch('/routing/prefetched.json').then((r) => r.json()); + return { message }; +} diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetched/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/prefetched/+page.svelte new file mode 100644 index 000000000000..65b84bb82fa9 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/prefetched/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.message}

diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetched/hash-route/+page.js b/packages/kit/test/apps/basics/src/routes/routing/prefetched/hash-route/+page.js new file mode 100644 index 000000000000..81da9fc7ae01 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/prefetched/hash-route/+page.js @@ -0,0 +1,6 @@ +let load_calls = 0; + +export const load = () => { + load_calls += 1; + return { calls: load_calls }; +}; diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetching/hash-route.svelte b/packages/kit/test/apps/basics/src/routes/routing/prefetched/hash-route/+page.svelte similarity index 69% rename from packages/kit/test/apps/basics/src/routes/routing/prefetching/hash-route.svelte rename to packages/kit/test/apps/basics/src/routes/routing/prefetched/hash-route/+page.svelte index 39fa611fe77e..dd0428be8d53 100644 --- a/packages/kit/test/apps/basics/src/routes/routing/prefetching/hash-route.svelte +++ b/packages/kit/test/apps/basics/src/routes/routing/prefetched/hash-route/+page.svelte @@ -1,17 +1,8 @@ - - - - - -

{message}

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetching/hash-route/+page.js b/packages/kit/test/apps/basics/src/routes/routing/prefetching/hash-route/+page.js new file mode 100644 index 000000000000..81da9fc7ae01 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/prefetching/hash-route/+page.js @@ -0,0 +1,6 @@ +let load_calls = 0; + +export const load = () => { + load_calls += 1; + return { calls: load_calls }; +}; diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetched/hash-route.svelte b/packages/kit/test/apps/basics/src/routes/routing/prefetching/hash-route/+page.svelte similarity index 69% rename from packages/kit/test/apps/basics/src/routes/routing/prefetched/hash-route.svelte rename to packages/kit/test/apps/basics/src/routes/routing/prefetching/hash-route/+page.svelte index 39fa611fe77e..dd0428be8d53 100644 --- a/packages/kit/test/apps/basics/src/routes/routing/prefetched/hash-route.svelte +++ b/packages/kit/test/apps/basics/src/routes/routing/prefetching/hash-route/+page.svelte @@ -1,17 +1,8 @@ - - - - - -

{message}

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetched/+page.js b/packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetched/+page.js new file mode 100644 index 000000000000..76a9f0c1cada --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetched/+page.js @@ -0,0 +1,5 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const message = await fetch('/routing/prefetched.json').then((r) => r.json()); + return { message }; +} diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetched/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetched/+page.svelte new file mode 100644 index 000000000000..65b84bb82fa9 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetched/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.message}

diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/+page.js b/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/+page.js new file mode 100644 index 000000000000..fa3fb83763d2 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/+page.js @@ -0,0 +1,5 @@ +/** @type {import('@sveltejs/kit').Load} */ +export function load({ params }) { + const { rest } = params; + return { rest }; +} diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/+page.svelte similarity index 56% rename from packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/index.svelte rename to packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/+page.svelte index 9bbca8792006..34cbcd0e13d2 100644 --- a/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/index.svelte +++ b/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/+page.svelte @@ -1,20 +1,12 @@ - -

{$page.params.rest}

-

{rest}

+

{data.rest}

deep abc diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep.json.js b/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep.json/+server.js similarity index 72% rename from packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep.json.js rename to packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep.json/+server.js index 482b5d02a05b..e95ee43a2029 100644 --- a/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep.json.js +++ b/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep.json/+server.js @@ -1,4 +1,4 @@ /** @type {import('@sveltejs/kit').RequestHandler} */ export function GET({ params }) { - return { body: params.rest }; + return new Response(params.rest); } diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep.svelte b/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep.svelte deleted file mode 100644 index 46292ff04b93..000000000000 --- a/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - - - -

{$page.params.rest}

-

{rest}

- -deep -back diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep/+page.js b/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep/+page.js new file mode 100644 index 000000000000..fa3fb83763d2 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep/+page.js @@ -0,0 +1,5 @@ +/** @type {import('@sveltejs/kit').Load} */ +export function load({ params }) { + const { rest } = params; + return { rest }; +} diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep/+page.svelte new file mode 100644 index 000000000000..9d2827def5c1 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/rest/[...rest]/deep/+page.svelte @@ -0,0 +1,12 @@ + + +

{$page.params.rest}

+

{data.rest}

+ +deep +back diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/complex/[...parts].json.js b/packages/kit/test/apps/basics/src/routes/routing/rest/complex/[...parts].json.js deleted file mode 100644 index e873880aff69..000000000000 --- a/packages/kit/test/apps/basics/src/routes/routing/rest/complex/[...parts].json.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET({ params }) { - return { - body: { - parts: params.parts - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/complex/[...parts].json/+server.js b/packages/kit/test/apps/basics/src/routes/routing/rest/complex/[...parts].json/+server.js new file mode 100644 index 000000000000..ea35e0a50119 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/rest/complex/[...parts].json/+server.js @@ -0,0 +1,8 @@ +import { json } from '@sveltejs/kit'; + +/** @type {import('./$types').RequestHandler} */ +export function GET({ params }) { + return json({ + parts: params.parts + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/complex/prefix-[...parts].svelte b/packages/kit/test/apps/basics/src/routes/routing/rest/complex/prefix-[...parts].svelte deleted file mode 100644 index 0ebdc7f62dbe..000000000000 --- a/packages/kit/test/apps/basics/src/routes/routing/rest/complex/prefix-[...parts].svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - - -

parts: {parts}

diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/complex/prefix-[...parts]/+page.js b/packages/kit/test/apps/basics/src/routes/routing/rest/complex/prefix-[...parts]/+page.js new file mode 100644 index 000000000000..1c3e434414c7 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/rest/complex/prefix-[...parts]/+page.js @@ -0,0 +1,6 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch, params }) { + const res = await fetch(`/routing/rest/complex/${params.parts}.json`); + const { parts } = await res.json(); + return { parts }; +} diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/complex/prefix-[...parts]/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/rest/complex/prefix-[...parts]/+page.svelte new file mode 100644 index 000000000000..f6da905e2de4 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/rest/complex/prefix-[...parts]/+page.svelte @@ -0,0 +1,6 @@ + + +

parts: {data.parts}

diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/__layout.svelte b/packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/+layout.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/__layout.svelte rename to packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/about.svelte b/packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/about.svelte rename to packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/[dynamic]-bar/[...rest].svelte b/packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/[dynamic]-bar/[...rest]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/[dynamic]-bar/[...rest].svelte rename to packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/[dynamic]-bar/[...rest]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/foo/[...rest].svelte b/packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/foo/[...rest]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/foo/[...rest].svelte rename to packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/foo/[...rest]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/rest/non-greedy/index.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/path/__layout.svelte b/packages/kit/test/apps/basics/src/routes/routing/rest/path/+layout.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/rest/path/__layout.svelte rename to packages/kit/test/apps/basics/src/routes/routing/rest/path/+layout.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/path/[...ignored].svelte b/packages/kit/test/apps/basics/src/routes/routing/rest/path/[...ignored].svelte deleted file mode 100644 index 9ec935d3b738..000000000000 --- a/packages/kit/test/apps/basics/src/routes/routing/rest/path/[...ignored].svelte +++ /dev/null @@ -1,17 +0,0 @@ - - - - -

path: {path}

diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/path/[...ignored]/+page.js b/packages/kit/test/apps/basics/src/routes/routing/rest/path/[...ignored]/+page.js new file mode 100644 index 000000000000..6f91b280d8b9 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/rest/path/[...ignored]/+page.js @@ -0,0 +1,5 @@ +/** @type {import('@sveltejs/kit').Load} */ +export function load({ url }) { + const { pathname: path } = url; + return { path }; +} diff --git a/packages/kit/test/apps/basics/src/routes/routing/rest/path/[...ignored]/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/rest/path/[...ignored]/+page.svelte new file mode 100644 index 000000000000..07478c851aa6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/rest/path/[...ignored]/+page.svelte @@ -0,0 +1,6 @@ + + +

path: {data.path}

diff --git a/packages/kit/test/apps/basics/src/routes/routing/route-id/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/route-id/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/route-id/index.svelte rename to packages/kit/test/apps/basics/src/routes/routing/route-id/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/route-id/[x].svelte b/packages/kit/test/apps/basics/src/routes/routing/route-id/[x].svelte deleted file mode 100644 index 972fff95db14..000000000000 --- a/packages/kit/test/apps/basics/src/routes/routing/route-id/[x].svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - - -

routeId in load: {routeId}

-

routeId in store: {$page.routeId}

diff --git a/packages/kit/test/apps/basics/src/routes/routing/route-id/[x]/+page.js b/packages/kit/test/apps/basics/src/routes/routing/route-id/[x]/+page.js new file mode 100644 index 000000000000..a5de040622e7 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/route-id/[x]/+page.js @@ -0,0 +1,4 @@ +/** @type {import('./$types').PageLoad} */ +export function load({ routeId }) { + return { routeId }; +} diff --git a/packages/kit/test/apps/basics/src/routes/routing/route-id/[x]/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/route-id/[x]/+page.svelte new file mode 100644 index 000000000000..b8b9e72c71bd --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/route-id/[x]/+page.svelte @@ -0,0 +1,9 @@ + + +

routeId in load: {data.routeId}

+

routeId in store: {$page.routeId}

diff --git a/packages/kit/test/apps/basics/src/routes/routing/shadow-dom/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/shadow-dom/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/shadow-dom/index.svelte rename to packages/kit/test/apps/basics/src/routes/routing/shadow-dom/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/skipped/[one]/[two].svelte b/packages/kit/test/apps/basics/src/routes/routing/skipped/[one]/[two].svelte deleted file mode 100644 index 8ba2d8ac7cef..000000000000 --- a/packages/kit/test/apps/basics/src/routes/routing/skipped/[one]/[two].svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - - -

{one}/{two}

- -next diff --git a/packages/kit/test/apps/basics/src/routes/routing/skipped/[one]/[two]/+page.js b/packages/kit/test/apps/basics/src/routes/routing/skipped/[one]/[two]/+page.js new file mode 100644 index 000000000000..c919ae378c59 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/skipped/[one]/[two]/+page.js @@ -0,0 +1,4 @@ +/** @type {import('@sveltejs/kit').Load} */ +export function load({ params }) { + return params; +} diff --git a/packages/kit/test/apps/basics/src/routes/routing/skipped/[one]/[two]/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/skipped/[one]/[two]/+page.svelte new file mode 100644 index 000000000000..4aa7ead7772f --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/skipped/[one]/[two]/+page.svelte @@ -0,0 +1,8 @@ + + +

{data.one}/{data.two}

+ +next diff --git a/packages/kit/test/apps/basics/src/routes/routing/slashes/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/slashes/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/slashes/index.svelte rename to packages/kit/test/apps/basics/src/routes/routing/slashes/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/split-params/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/split-params/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/split-params/index.svelte rename to packages/kit/test/apps/basics/src/routes/routing/split-params/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/split-params/[a]-[b].svelte b/packages/kit/test/apps/basics/src/routes/routing/split-params/[a]-[b]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/routing/split-params/[a]-[b].svelte rename to packages/kit/test/apps/basics/src/routes/routing/split-params/[a]-[b]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/scroll/cross-document/a/+page.js b/packages/kit/test/apps/basics/src/routes/scroll/cross-document/a/+page.js new file mode 100644 index 000000000000..90f739775e8b --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/scroll/cross-document/a/+page.js @@ -0,0 +1 @@ +export const router = false; diff --git a/packages/kit/test/apps/basics/src/routes/scroll/cross-document/a.svelte b/packages/kit/test/apps/basics/src/routes/scroll/cross-document/a/+page.svelte similarity index 60% rename from packages/kit/test/apps/basics/src/routes/scroll/cross-document/a.svelte rename to packages/kit/test/apps/basics/src/routes/scroll/cross-document/a/+page.svelte index 485fc284e3ea..902e783cdb8b 100644 --- a/packages/kit/test/apps/basics/src/routes/scroll/cross-document/a.svelte +++ b/packages/kit/test/apps/basics/src/routes/scroll/cross-document/a/+page.svelte @@ -1,7 +1,3 @@ - -

a

diff --git a/packages/kit/test/apps/basics/src/routes/scroll/cross-document/b.svelte b/packages/kit/test/apps/basics/src/routes/scroll/cross-document/b.svelte deleted file mode 100644 index 02808b2e9ea5..000000000000 --- a/packages/kit/test/apps/basics/src/routes/scroll/cross-document/b.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -

b

- -c diff --git a/packages/kit/test/apps/basics/src/routes/scroll/cross-document/b/+page.js b/packages/kit/test/apps/basics/src/routes/scroll/cross-document/b/+page.js new file mode 100644 index 000000000000..b33c2aee87a1 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/scroll/cross-document/b/+page.js @@ -0,0 +1 @@ +export const router = true; diff --git a/packages/kit/test/apps/basics/src/routes/scroll/cross-document/b/+page.svelte b/packages/kit/test/apps/basics/src/routes/scroll/cross-document/b/+page.svelte new file mode 100644 index 000000000000..c065e2362f6a --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/scroll/cross-document/b/+page.svelte @@ -0,0 +1,3 @@ +

b

+ +c diff --git a/packages/kit/test/apps/basics/src/routes/scroll/cross-document/c.svelte b/packages/kit/test/apps/basics/src/routes/scroll/cross-document/c.svelte deleted file mode 100644 index 55191947cd2e..000000000000 --- a/packages/kit/test/apps/basics/src/routes/scroll/cross-document/c.svelte +++ /dev/null @@ -1,5 +0,0 @@ - - -

c

diff --git a/packages/kit/test/apps/basics/src/routes/scroll/cross-document/c/+page.js b/packages/kit/test/apps/basics/src/routes/scroll/cross-document/c/+page.js new file mode 100644 index 000000000000..b33c2aee87a1 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/scroll/cross-document/c/+page.js @@ -0,0 +1 @@ +export const router = true; diff --git a/packages/kit/test/apps/basics/src/routes/scroll/cross-document/c/+page.svelte b/packages/kit/test/apps/basics/src/routes/scroll/cross-document/c/+page.svelte new file mode 100644 index 000000000000..232c276f86cb --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/scroll/cross-document/c/+page.svelte @@ -0,0 +1 @@ +

c

diff --git a/packages/kit/test/apps/basics/src/routes/selection/__layout.svelte b/packages/kit/test/apps/basics/src/routes/selection/+layout.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/selection/__layout.svelte rename to packages/kit/test/apps/basics/src/routes/selection/+layout.svelte diff --git a/packages/kit/test/apps/basics/src/routes/selection/a.svelte b/packages/kit/test/apps/basics/src/routes/selection/a/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/selection/a.svelte rename to packages/kit/test/apps/basics/src/routes/selection/a/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/selection/b.svelte b/packages/kit/test/apps/basics/src/routes/selection/b/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/selection/b.svelte rename to packages/kit/test/apps/basics/src/routes/selection/b/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/session/+page.js b/packages/kit/test/apps/basics/src/routes/session/+page.js new file mode 100644 index 000000000000..ac75fa4c072c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/session/+page.js @@ -0,0 +1,6 @@ +/** @type {import('@sveltejs/kit').Load} */ +export function load({ session }) { + return { + answer: session.answer + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/session/+page.svelte b/packages/kit/test/apps/basics/src/routes/session/+page.svelte new file mode 100644 index 000000000000..8a4b5c120b6d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/session/+page.svelte @@ -0,0 +1,17 @@ + + +

answer via props: {data.answer}

+

answer via store: {$session.answer}

+{#if data.answer === 43} +

answer via props is 43

+{/if} +{#if $session.answer === 43} +

answer via store is 43

+{/if} + + diff --git a/packages/kit/test/apps/basics/src/routes/session/index.svelte b/packages/kit/test/apps/basics/src/routes/session/index.svelte deleted file mode 100644 index 52d57698028e..000000000000 --- a/packages/kit/test/apps/basics/src/routes/session/index.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - - - -

answer via props: {answer}

-

answer via store: {$session.answer}

-{#if answer === 43} -

answer via props is 43

-{/if} -{#if $session.answer === 43} -

answer via store is 43

-{/if} - - diff --git a/packages/kit/test/apps/basics/src/routes/set-cookie/+server.js b/packages/kit/test/apps/basics/src/routes/set-cookie/+server.js new file mode 100644 index 000000000000..4e63c7ad877d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/set-cookie/+server.js @@ -0,0 +1,8 @@ +export function GET() { + const headers = new Headers(); + headers.append('set-cookie', 'answer=42; HttpOnly'); + headers.append('set-cookie', 'problem=comma, separated, values; HttpOnly'); + return new Response(undefined, { + headers + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/set-cookie/index.js b/packages/kit/test/apps/basics/src/routes/set-cookie/index.js deleted file mode 100644 index cf84dd71a943..000000000000 --- a/packages/kit/test/apps/basics/src/routes/set-cookie/index.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - headers: { - 'set-cookie': ['answer=42; HttpOnly', 'problem=comma, separated, values; HttpOnly'] - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/index.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/index.svelte rename to packages/kit/test/apps/basics/src/routes/shadowed/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/dynamic/[slug].js b/packages/kit/test/apps/basics/src/routes/shadowed/dynamic/[slug].js deleted file mode 100644 index c87687948469..000000000000 --- a/packages/kit/test/apps/basics/src/routes/shadowed/dynamic/[slug].js +++ /dev/null @@ -1,6 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET({ params }) { - return { - body: params - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/dynamic/[slug]/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/dynamic/[slug]/+page.server.js new file mode 100644 index 000000000000..66e42cc3b106 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/shadowed/dynamic/[slug]/+page.server.js @@ -0,0 +1,4 @@ +/** @type {import('./$types').PageServerLoad} */ +export function load({ params }) { + return params; +} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/dynamic/[slug].svelte b/packages/kit/test/apps/basics/src/routes/shadowed/dynamic/[slug]/+page.svelte similarity index 53% rename from packages/kit/test/apps/basics/src/routes/shadowed/dynamic/[slug].svelte rename to packages/kit/test/apps/basics/src/routes/shadowed/dynamic/[slug]/+page.svelte index 5101e9c07f18..73be1987abbc 100644 --- a/packages/kit/test/apps/basics/src/routes/shadowed/dynamic/[slug].svelte +++ b/packages/kit/test/apps/basics/src/routes/shadowed/dynamic/[slug]/+page.svelte @@ -1,9 +1,9 @@ -

slug: {slug}

+

slug: {data.slug}

foo bar diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/error-get.js b/packages/kit/test/apps/basics/src/routes/shadowed/error-get.js deleted file mode 100644 index ef4a8ef17cf6..000000000000 --- a/packages/kit/test/apps/basics/src/routes/shadowed/error-get.js +++ /dev/null @@ -1,5 +0,0 @@ -export function GET() { - return { - status: 404 - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/error-get/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/error-get/+page.server.js new file mode 100644 index 000000000000..1ebbb83f57e6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/shadowed/error-get/+page.server.js @@ -0,0 +1,5 @@ +import { error } from '@sveltejs/kit'; + +export function load() { + throw error(404, undefined); +} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/error-get.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/error-get/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/error-get.svelte rename to packages/kit/test/apps/basics/src/routes/shadowed/error-get/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/error-post.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/error-post.svelte deleted file mode 100644 index 2886e86d0808..000000000000 --- a/packages/kit/test/apps/basics/src/routes/shadowed/error-post.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - -

{get_message} / {post_message}

-

status: {$page.status}

diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/error-post.js b/packages/kit/test/apps/basics/src/routes/shadowed/error-post/+page.server.js similarity index 69% rename from packages/kit/test/apps/basics/src/routes/shadowed/error-post.js rename to packages/kit/test/apps/basics/src/routes/shadowed/error-post/+page.server.js index 51b826ed36a6..7bc1eeac4928 100644 --- a/packages/kit/test/apps/basics/src/routes/shadowed/error-post.js +++ b/packages/kit/test/apps/basics/src/routes/shadowed/error-post/+page.server.js @@ -1,8 +1,6 @@ -export function GET() { +export function load() { return { - body: { - get_message: 'hello from get' - } + get_message: 'hello from get' }; } @@ -11,7 +9,7 @@ export async function POST({ request }) { return { status: 400, - body: { + errors: { post_message: `echo: ${data.get('message')}` } }; diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/error-post/+page.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/error-post/+page.svelte new file mode 100644 index 000000000000..246542429da8 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/shadowed/error-post/+page.svelte @@ -0,0 +1,12 @@ + + +

{data.get_message} / {errors.post_message}

+

status: {$page.status}

diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/missing-get/index.js b/packages/kit/test/apps/basics/src/routes/shadowed/missing-get/+page.server.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/missing-get/index.js rename to packages/kit/test/apps/basics/src/routes/shadowed/missing-get/+page.server.js diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/missing-get/index.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/missing-get/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/missing-get/index.svelte rename to packages/kit/test/apps/basics/src/routes/shadowed/missing-get/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/no-get.js b/packages/kit/test/apps/basics/src/routes/shadowed/no-get/+page.server.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/no-get.js rename to packages/kit/test/apps/basics/src/routes/shadowed/no-get/+page.server.js diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/no-get.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/no-get/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/no-get.svelte rename to packages/kit/test/apps/basics/src/routes/shadowed/no-get/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get-with-cookie.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get-with-cookie.js deleted file mode 100644 index 0032fd788dd1..000000000000 --- a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get-with-cookie.js +++ /dev/null @@ -1,9 +0,0 @@ -export function GET() { - return { - status: 302, - headers: { - location: '/shadowed/redirected', - 'set-cookie': 'shadow-redirect=happy' - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get-with-cookie/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get-with-cookie/+page.server.js new file mode 100644 index 000000000000..2f479b88bc52 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get-with-cookie/+page.server.js @@ -0,0 +1,6 @@ +import { redirect } from '@sveltejs/kit'; + +export function load({ setHeaders }) { + setHeaders({ 'set-cookie': 'shadow-redirect=happy' }); + throw redirect(302, '/shadowed/redirected'); +} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get-with-cookie.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get-with-cookie/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/redirect-get-with-cookie.svelte rename to packages/kit/test/apps/basics/src/routes/shadowed/redirect-get-with-cookie/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get.js deleted file mode 100644 index ff759e497edc..000000000000 --- a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get.js +++ /dev/null @@ -1,8 +0,0 @@ -export function GET() { - return { - status: 302, - headers: { - location: '/shadowed/redirected' - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get/+page.server.js new file mode 100644 index 000000000000..2da446ff6757 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get/+page.server.js @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export function load() { + throw redirect(302, '/shadowed/redirected'); +} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/redirect-get.svelte rename to packages/kit/test/apps/basics/src/routes/shadowed/redirect-get/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post-with-cookie.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post-with-cookie.js deleted file mode 100644 index c8a25b0d6a2f..000000000000 --- a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post-with-cookie.js +++ /dev/null @@ -1,9 +0,0 @@ -export function POST() { - return { - status: 302, - headers: { - location: '/shadowed/redirected', - 'set-cookie': 'shadow-redirect=happy' - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post-with-cookie/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post-with-cookie/+page.server.js new file mode 100644 index 000000000000..bdd7b53b5995 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post-with-cookie/+page.server.js @@ -0,0 +1,6 @@ +import { redirect } from '@sveltejs/kit'; + +export function POST({ setHeaders }) { + setHeaders({ 'set-cookie': 'shadow-redirect=happy' }); + throw redirect(302, '/shadowed/redirected'); +} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post-with-cookie.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post-with-cookie/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/redirect-post-with-cookie.svelte rename to packages/kit/test/apps/basics/src/routes/shadowed/redirect-post-with-cookie/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post.js deleted file mode 100644 index 937f66a0afcd..000000000000 --- a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post.js +++ /dev/null @@ -1,8 +0,0 @@ -export function POST() { - return { - status: 302, - headers: { - location: '/shadowed/redirected' - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post/+page.server.js new file mode 100644 index 000000000000..2b8b1976028e --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post/+page.server.js @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export function POST() { + throw redirect(302, '/shadowed/redirected'); +} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/redirect-post.svelte rename to packages/kit/test/apps/basics/src/routes/shadowed/redirect-post/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect/index.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/redirect/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/redirect/index.svelte rename to packages/kit/test/apps/basics/src/routes/shadowed/redirect/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect/[a].js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect/[a].js deleted file mode 100644 index fa153fd1f8a7..000000000000 --- a/packages/kit/test/apps/basics/src/routes/shadowed/redirect/[a].js +++ /dev/null @@ -1,8 +0,0 @@ -export function GET() { - return { - status: 302, - headers: { - location: '/shadowed/redirect/c' - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect/[a]/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect/[a]/+page.server.js new file mode 100644 index 000000000000..266eee1cd371 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/shadowed/redirect/[a]/+page.server.js @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export function load() { + throw redirect(302, '/shadowed/redirect/c'); +} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect/[a].svelte b/packages/kit/test/apps/basics/src/routes/shadowed/redirect/[a]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/redirect/[a].svelte rename to packages/kit/test/apps/basics/src/routes/shadowed/redirect/[a]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect/[b].svelte b/packages/kit/test/apps/basics/src/routes/shadowed/redirect/[b]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/redirect/[b].svelte rename to packages/kit/test/apps/basics/src/routes/shadowed/redirect/[b]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect/c.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/redirect/c/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/redirect/c.svelte rename to packages/kit/test/apps/basics/src/routes/shadowed/redirect/c/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirected.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirected.js deleted file mode 100644 index ace2acc46c30..000000000000 --- a/packages/kit/test/apps/basics/src/routes/shadowed/redirected.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - status: 'successful' - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirected.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/redirected.svelte deleted file mode 100644 index 97ba078a78db..000000000000 --- a/packages/kit/test/apps/basics/src/routes/shadowed/redirected.svelte +++ /dev/null @@ -1,6 +0,0 @@ - - -

Redirection was {status}

diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirected/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirected/+page.server.js new file mode 100644 index 000000000000..b24cecc80800 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/shadowed/redirected/+page.server.js @@ -0,0 +1,5 @@ +export function load() { + return { + status: 'successful' + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirected/+page.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/redirected/+page.svelte new file mode 100644 index 000000000000..de7f4514f65d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/shadowed/redirected/+page.svelte @@ -0,0 +1,6 @@ + + +

Redirection was {data.status}

diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/same-render-entry.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/same-render-entry/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/shadowed/same-render-entry.svelte rename to packages/kit/test/apps/basics/src/routes/shadowed/same-render-entry/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/same-render.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/same-render.svelte deleted file mode 100644 index d3cc398cd527..000000000000 --- a/packages/kit/test/apps/basics/src/routes/shadowed/same-render.svelte +++ /dev/null @@ -1,5 +0,0 @@ - -

URL: {url}

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/same-render/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/same-render/+page.server.js new file mode 100644 index 000000000000..7ae83dbcd128 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/shadowed/same-render/+page.server.js @@ -0,0 +1,4 @@ +/** @type {import('./$types').PageServerLoad} */ +export function load({ url }) { + return { url: url.toString() }; +} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/same-render/+page.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/same-render/+page.svelte new file mode 100644 index 000000000000..4ae6d874964b --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/shadowed/same-render/+page.svelte @@ -0,0 +1,6 @@ + + +

URL: {data.url}

diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/simple.js b/packages/kit/test/apps/basics/src/routes/shadowed/simple.js deleted file mode 100644 index 7492c9d60550..000000000000 --- a/packages/kit/test/apps/basics/src/routes/shadowed/simple.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET({ locals }) { - return { - body: { - answer: locals.answer - } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/simple.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/simple.svelte deleted file mode 100644 index f5f46beb9dcf..000000000000 --- a/packages/kit/test/apps/basics/src/routes/shadowed/simple.svelte +++ /dev/null @@ -1,6 +0,0 @@ - - -

The answer is {answer}

diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/simple/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/simple/+page.server.js new file mode 100644 index 000000000000..40e4abcd22d3 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/shadowed/simple/+page.server.js @@ -0,0 +1,6 @@ +/** @type {import('./$types').PageServerLoad} */ +export function load({ locals }) { + return { + answer: locals.answer + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/simple/+page.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/simple/+page.svelte new file mode 100644 index 000000000000..a475b1237f27 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/shadowed/simple/+page.svelte @@ -0,0 +1,6 @@ + + +

The answer is {data.answer}

diff --git a/packages/kit/test/apps/basics/src/routes/src/[...anything].js b/packages/kit/test/apps/basics/src/routes/src/[...anything].js deleted file mode 100644 index 2186b0cf4c94..000000000000 --- a/packages/kit/test/apps/basics/src/routes/src/[...anything].js +++ /dev/null @@ -1,6 +0,0 @@ -/** @type {import('./__types/[...anything]').RequestHandler} */ -export function GET() { - return { - body: 'dynamically rendered file' - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/src/[...anything]/+server.js b/packages/kit/test/apps/basics/src/routes/src/[...anything]/+server.js new file mode 100644 index 000000000000..8235726aacf7 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/src/[...anything]/+server.js @@ -0,0 +1,4 @@ +/** @type {import('./$types').RequestHandler} */ +export function GET() { + return new Response('dynamically rendered file'); +} diff --git a/packages/kit/test/apps/basics/src/routes/static.svelte b/packages/kit/test/apps/basics/src/routes/static/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/static.svelte rename to packages/kit/test/apps/basics/src/routes/static/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/store/index.svelte b/packages/kit/test/apps/basics/src/routes/store/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/store/index.svelte rename to packages/kit/test/apps/basics/src/routes/store/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/store/data/+error.svelte b/packages/kit/test/apps/basics/src/routes/store/data/+error.svelte new file mode 100644 index 000000000000..9f5c4cf0a40d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/data/+error.svelte @@ -0,0 +1,6 @@ + + + diff --git a/packages/kit/test/apps/basics/src/routes/store/data/+layout.js b/packages/kit/test/apps/basics/src/routes/store/data/+layout.js new file mode 100644 index 000000000000..5d007f4b6557 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/data/+layout.js @@ -0,0 +1,6 @@ +export function load() { + return { + name: 'SvelteKit', + value: 123 + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/store/data/+layout.svelte b/packages/kit/test/apps/basics/src/routes/store/data/+layout.svelte new file mode 100644 index 000000000000..1ad81a9ca762 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/data/+layout.svelte @@ -0,0 +1,13 @@ + + +
{JSON.stringify($page.data)}
+
{$page.error?.message}
+ + + + diff --git a/packages/kit/test/apps/basics/src/routes/store/data/[item]/+page.js b/packages/kit/test/apps/basics/src/routes/store/data/[item]/+page.js new file mode 100644 index 000000000000..fd39f31849ab --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/data/[item]/+page.js @@ -0,0 +1,17 @@ +import { error } from '@sveltejs/kit'; + +/** @type {import('./$types').PageLoad} */ +export function load({ params }) { + if (params.item === 'xxx') { + throw error(500, 'Params = xxx'); + } + + if (params.item === 'yyy') { + throw error(500, 'Params = yyy'); + } + + return { + page: params.item, + value: 456 + }; +} diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/index.svelte b/packages/kit/test/apps/basics/src/routes/store/data/[item]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/index.svelte rename to packages/kit/test/apps/basics/src/routes/store/data/[item]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/store/data/foo/+page.js b/packages/kit/test/apps/basics/src/routes/store/data/foo/+page.js new file mode 100644 index 000000000000..9a1976ddf63d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/data/foo/+page.js @@ -0,0 +1,18 @@ +let is_first = true; + +export function load({ url }) { + if (url.searchParams.get('reset')) { + is_first = true; + return {}; + } + + if (is_first) { + is_first = false; + throw new Error('uh oh'); + } + + return { + foo: true, + number: 2 + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/store/data/foo/+page.svelte b/packages/kit/test/apps/basics/src/routes/store/data/foo/+page.svelte new file mode 100644 index 000000000000..5d4378ac043d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/data/foo/+page.svelte @@ -0,0 +1,7 @@ + + +

stuff - foo

+

Number prop: {data.number}

+store diff --git a/packages/kit/test/apps/basics/src/routes/store/data/only-data-changes/+page.js b/packages/kit/test/apps/basics/src/routes/store/data/only-data-changes/+page.js new file mode 100644 index 000000000000..3db2ae6fe64b --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/data/only-data-changes/+page.js @@ -0,0 +1,3 @@ +export function load({ session }) { + return session; +} diff --git a/packages/kit/test/apps/basics/src/routes/store/data/only-data-changes/+page.svelte b/packages/kit/test/apps/basics/src/routes/store/data/only-data-changes/+page.svelte new file mode 100644 index 000000000000..46978761d148 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/data/only-data-changes/+page.svelte @@ -0,0 +1,9 @@ + + +
{JSON.stringify(data)}
+
{JSON.stringify($page.data)}
+ + diff --git a/packages/kit/test/apps/basics/src/routes/store/navigating/__layout.svelte b/packages/kit/test/apps/basics/src/routes/store/navigating/+layout.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/store/navigating/__layout.svelte rename to packages/kit/test/apps/basics/src/routes/store/navigating/+layout.svelte diff --git a/packages/kit/test/apps/basics/src/routes/store/navigating/a.svelte b/packages/kit/test/apps/basics/src/routes/store/navigating/a/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/store/navigating/a.svelte rename to packages/kit/test/apps/basics/src/routes/store/navigating/a/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/store/navigating/b.svelte b/packages/kit/test/apps/basics/src/routes/store/navigating/b.svelte deleted file mode 100644 index 23aaf604c196..000000000000 --- a/packages/kit/test/apps/basics/src/routes/store/navigating/b.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - -

b

diff --git a/packages/kit/test/apps/basics/src/routes/store/navigating/b/+page.js b/packages/kit/test/apps/basics/src/routes/store/navigating/b/+page.js new file mode 100644 index 000000000000..7b1c80573a3f --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/navigating/b/+page.js @@ -0,0 +1,5 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load() { + await new Promise((f) => setTimeout(f, 50)); + return {}; +} diff --git a/packages/kit/test/apps/basics/src/routes/store/navigating/b/+page.svelte b/packages/kit/test/apps/basics/src/routes/store/navigating/b/+page.svelte new file mode 100644 index 000000000000..db2e4f2757c0 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/navigating/b/+page.svelte @@ -0,0 +1 @@ +

b

diff --git a/packages/kit/test/apps/basics/src/routes/store/navigating/c.svelte b/packages/kit/test/apps/basics/src/routes/store/navigating/c.svelte deleted file mode 100644 index 1f77e0d41c38..000000000000 --- a/packages/kit/test/apps/basics/src/routes/store/navigating/c.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - -

c

diff --git a/packages/kit/test/apps/basics/src/routes/store/navigating/c/+page.js b/packages/kit/test/apps/basics/src/routes/store/navigating/c/+page.js new file mode 100644 index 000000000000..dbc433ad4c42 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/navigating/c/+page.js @@ -0,0 +1,5 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load() { + await new Promise((f) => setTimeout(f, 1000)); + return {}; +} diff --git a/packages/kit/test/apps/basics/src/routes/store/navigating/c/+page.svelte b/packages/kit/test/apps/basics/src/routes/store/navigating/c/+page.svelte new file mode 100644 index 000000000000..2b98ed2e7228 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/navigating/c/+page.svelte @@ -0,0 +1 @@ +

c

diff --git a/packages/kit/test/apps/basics/src/routes/store/result.svelte b/packages/kit/test/apps/basics/src/routes/store/result/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/store/result.svelte rename to packages/kit/test/apps/basics/src/routes/store/result/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/[item].svelte b/packages/kit/test/apps/basics/src/routes/store/stuff/[item].svelte deleted file mode 100644 index fa9f0b950739..000000000000 --- a/packages/kit/test/apps/basics/src/routes/store/stuff/[item].svelte +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/__error.svelte b/packages/kit/test/apps/basics/src/routes/store/stuff/__error.svelte deleted file mode 100644 index 30e4eac8f3e1..000000000000 --- a/packages/kit/test/apps/basics/src/routes/store/stuff/__error.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - - - diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/__layout.svelte b/packages/kit/test/apps/basics/src/routes/store/stuff/__layout.svelte deleted file mode 100644 index 8d1245976f1d..000000000000 --- a/packages/kit/test/apps/basics/src/routes/store/stuff/__layout.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - - - -
{JSON.stringify($page.stuff)}
- - - - diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/foo.svelte b/packages/kit/test/apps/basics/src/routes/store/stuff/foo.svelte deleted file mode 100644 index 50db1f7d25a3..000000000000 --- a/packages/kit/test/apps/basics/src/routes/store/stuff/foo.svelte +++ /dev/null @@ -1,32 +0,0 @@ - - - - -

stuff - foo

-

Number prop: {number}

-store diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/[id].svelte b/packages/kit/test/apps/basics/src/routes/transform-page-chunk/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/[id].svelte rename to packages/kit/test/apps/basics/src/routes/transform-page-chunk/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/transform-page-chunk/index.svelte b/packages/kit/test/apps/basics/src/routes/transform-page-chunk/index.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/test/apps/basics/src/routes/unsafe-replacement/+page.js b/packages/kit/test/apps/basics/src/routes/unsafe-replacement/+page.js new file mode 100644 index 000000000000..a690410af77b --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/unsafe-replacement/+page.js @@ -0,0 +1,3 @@ +export function load() { + return { escape: '$&' }; +} diff --git a/packages/kit/test/apps/basics/src/routes/unsafe-replacement/+page.svelte b/packages/kit/test/apps/basics/src/routes/unsafe-replacement/+page.svelte new file mode 100644 index 000000000000..1cceb4edb36e --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/unsafe-replacement/+page.svelte @@ -0,0 +1,6 @@ + + +$& {data.escape} diff --git a/packages/kit/test/apps/basics/src/routes/unsafe-replacement/index.svelte b/packages/kit/test/apps/basics/src/routes/unsafe-replacement/index.svelte deleted file mode 100644 index 30072a1093a8..000000000000 --- a/packages/kit/test/apps/basics/src/routes/unsafe-replacement/index.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - - -$& {escape} diff --git a/packages/kit/test/apps/basics/src/routes/use-action/index.svelte b/packages/kit/test/apps/basics/src/routes/use-action/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/use-action/index.svelte rename to packages/kit/test/apps/basics/src/routes/use-action/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/use-action/focus-and-scroll.svelte b/packages/kit/test/apps/basics/src/routes/use-action/focus-and-scroll/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/use-action/focus-and-scroll.svelte rename to packages/kit/test/apps/basics/src/routes/use-action/focus-and-scroll/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/xss/index.json.js b/packages/kit/test/apps/basics/src/routes/xss.json/+server.js similarity index 57% rename from packages/kit/test/apps/basics/src/routes/xss/index.json.js rename to packages/kit/test/apps/basics/src/routes/xss.json/+server.js index 565fbb70b9c6..32e12abd887f 100644 --- a/packages/kit/test/apps/basics/src/routes/xss/index.json.js +++ b/packages/kit/test/apps/basics/src/routes/xss.json/+server.js @@ -1,9 +1,7 @@ +import { json } from '@sveltejs/kit'; + export function GET() { - const user = { + return json({ name: '' - }; - - return { - body: user - }; + }); } diff --git a/packages/kit/test/apps/basics/src/routes/xss/+page.js b/packages/kit/test/apps/basics/src/routes/xss/+page.js new file mode 100644 index 000000000000..bebc49e0026f --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/xss/+page.js @@ -0,0 +1,6 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const res = await fetch('/xss.json'); + const user = await res.json(); + return { user }; +} diff --git a/packages/kit/test/apps/basics/src/routes/xss/+page.svelte b/packages/kit/test/apps/basics/src/routes/xss/+page.svelte new file mode 100644 index 000000000000..f040a79acdb9 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/xss/+page.svelte @@ -0,0 +1,6 @@ + + +

user.name is {data.user.name}

diff --git a/packages/kit/test/apps/basics/src/routes/xss/[path].svelte b/packages/kit/test/apps/basics/src/routes/xss/[path]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/xss/[path].svelte rename to packages/kit/test/apps/basics/src/routes/xss/[path]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/xss/index.svelte b/packages/kit/test/apps/basics/src/routes/xss/index.svelte deleted file mode 100644 index 3ddbb25c8c2c..000000000000 --- a/packages/kit/test/apps/basics/src/routes/xss/index.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - - -

user.name is {user.name}

diff --git a/packages/kit/test/apps/basics/src/routes/xss/query.svelte b/packages/kit/test/apps/basics/src/routes/xss/query.svelte deleted file mode 100644 index aecb5a2e5180..000000000000 --- a/packages/kit/test/apps/basics/src/routes/xss/query.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - - - -
{JSON.stringify(values)}
-
{JSON.stringify(to_pojo($page.url.searchParams))}
diff --git a/packages/kit/test/apps/basics/src/routes/xss/query/+page.js b/packages/kit/test/apps/basics/src/routes/xss/query/+page.js new file mode 100644 index 000000000000..49fb18bd3229 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/xss/query/+page.js @@ -0,0 +1,8 @@ +import { to_pojo } from './utils.js'; + +/** @type {import('@sveltejs/kit').Load} */ +export function load({ url }) { + return { + values: to_pojo(url.searchParams) + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/xss/query/+page.svelte b/packages/kit/test/apps/basics/src/routes/xss/query/+page.svelte new file mode 100644 index 000000000000..52f9c4de4903 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/xss/query/+page.svelte @@ -0,0 +1,10 @@ + + +
{JSON.stringify(data.values)}
+
{JSON.stringify(to_pojo($page.url.searchParams))}
diff --git a/packages/kit/test/apps/basics/src/routes/xss/query/utils.js b/packages/kit/test/apps/basics/src/routes/xss/query/utils.js new file mode 100644 index 000000000000..97023e74857f --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/xss/query/utils.js @@ -0,0 +1,14 @@ +/** @typedef {Record} Query */ + +/** @param {URLSearchParams} query */ +export function to_pojo(query) { + /** @type {Query}*/ + const values = {}; + + query.forEach((value, key) => { + if (!(key in values)) values[key] = []; + values[key].push(value); + }); + + return values; +} diff --git a/packages/kit/test/apps/basics/src/routes/xss/shadow.js b/packages/kit/test/apps/basics/src/routes/xss/shadow.js deleted file mode 100644 index 91caaf8795c3..000000000000 --- a/packages/kit/test/apps/basics/src/routes/xss/shadow.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - const user = { - name: '' - }; - return { - body: { user } - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/xss/shadow.svelte b/packages/kit/test/apps/basics/src/routes/xss/shadow.svelte deleted file mode 100644 index 8ad2b30f8e4f..000000000000 --- a/packages/kit/test/apps/basics/src/routes/xss/shadow.svelte +++ /dev/null @@ -1,6 +0,0 @@ - -

user.name is {user.name}

- diff --git a/packages/kit/test/apps/basics/src/routes/xss/shadow/+page.server.js b/packages/kit/test/apps/basics/src/routes/xss/shadow/+page.server.js new file mode 100644 index 000000000000..e125d6a532c6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/xss/shadow/+page.server.js @@ -0,0 +1,7 @@ +/** @type {import('./$types').PageServerLoad} */ +export function load() { + const user = { + name: '' + }; + return { user }; +} diff --git a/packages/kit/test/apps/basics/src/routes/xss/shadow/+page.svelte b/packages/kit/test/apps/basics/src/routes/xss/shadow/+page.svelte new file mode 100644 index 000000000000..f040a79acdb9 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/xss/shadow/+page.svelte @@ -0,0 +1,6 @@ + + +

user.name is {data.user.name}

diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js index 9f5d51fb755a..4326213bc254 100644 --- a/packages/kit/test/apps/basics/test/client.test.js +++ b/packages/kit/test/apps/basics/test/client.test.js @@ -283,17 +283,7 @@ test.describe('Errors', () => { ); }); - test('client-side error from load() is a string', async ({ page }) => { - await page.goto('/errors/load-error-string-client'); - - expect(await page.textContent('footer')).toBe('Custom layout'); - expect(await page.textContent('#message')).toBe( - 'This is your custom error page saying: "Not found"' - ); - expect(await page.innerHTML('h1')).toBe('555'); - }); - - test('client-side error from load() is an Error', async ({ page }) => { + test('client-side error from load()', async ({ page }) => { await page.goto('/errors/load-error-client'); expect(await page.textContent('footer')).toBe('Custom layout'); @@ -303,21 +293,13 @@ test.describe('Errors', () => { expect(await page.innerHTML('h1')).toBe('555'); }); - test('client-side error from load() is malformed', async ({ page }) => { - await page.goto('/errors/load-error-malformed-client'); - - const body = await page.textContent('body'); - - expect(body).toMatch( - '"error" property returned from load() must be a string or instance of Error, received type "object"' - ); - }); - test('client-side 4xx status without error from load()', async ({ page }) => { await page.goto('/errors/load-status-without-error-client'); expect(await page.textContent('footer')).toBe('Custom layout'); - expect(await page.textContent('#message')).toBe('This is your custom error page saying: "401"'); + expect(await page.textContent('#message')).toBe( + 'This is your custom error page saying: "Error: 401"' + ); expect(await page.innerHTML('h1')).toBe('401'); }); }); @@ -335,27 +317,32 @@ test.describe('Load', () => { expect(await page.textContent('h2')).toBe('y: b: 1'); await app.goto('/load/change-detection/one/a'); - expect(await page.textContent('h2')).toBe('x: a: 1'); + expect(await page.textContent('h2')).toBe('x: a: 2'); await app.goto('/load/change-detection/one/b'); - expect(await page.textContent('h2')).toBe('x: b: 2'); + expect(await page.textContent('h2')).toBe('x: b: 3'); await app.invalidate('/load/change-detection/data.json'); expect(await page.textContent('h1')).toBe('layout loads: 2'); - expect(await page.textContent('h2')).toBe('x: b: 2'); + expect(await page.textContent('h2')).toBe('x: b: 3'); await app.invalidate('/load/change-detection/data.json'); expect(await page.textContent('h1')).toBe('layout loads: 3'); - expect(await page.textContent('h2')).toBe('x: b: 2'); + expect(await page.textContent('h2')).toBe('x: b: 3'); await app.invalidate('custom:change-detection-layout'); expect(await page.textContent('h1')).toBe('layout loads: 4'); - expect(await page.textContent('h2')).toBe('x: b: 2'); + expect(await page.textContent('h2')).toBe('x: b: 3'); - await page.click('button'); + await page.click('button:has-text("invalidate change-detection/data.json")'); await page.waitForFunction('window.invalidated'); expect(await page.textContent('h1')).toBe('layout loads: 5'); - expect(await page.textContent('h2')).toBe('x: b: 2'); + expect(await page.textContent('h2')).toBe('x: b: 3'); + + await page.click('button:has-text("invalidate all")'); + await page.waitForFunction('window.invalidated'); + expect(await page.textContent('h1')).toBe('layout loads: 6'); + expect(await page.textContent('h2')).toBe('x: b: 4'); }); test('load function is only called on session change when used in load', async ({ page }) => { @@ -408,7 +395,7 @@ test.describe('Load', () => { expect(await page.textContent('h1')).toBe('42'); expect(warnings).toContain( - `Loading http://localhost:${port}/load/window-fetch/data.json using \`window.fetch\`. For best results, use the \`fetch\` that is passed to your \`load\` function: https://kit.svelte.dev/docs/loading#input-fetch` + `Loading http://localhost:${port}/load/window-fetch/data.json using \`window.fetch\`. For best results, use the \`fetch\` that is passed to your \`load\` function: https://kit.svelte.dev/docs/load#input-fetch` ); warnings.length = 0; @@ -417,7 +404,7 @@ test.describe('Load', () => { expect(await page.textContent('h1')).toBe('42'); expect(warnings).not.toContain( - `Loading http://localhost:${port}/load/window-fetch/data.json using \`window.fetch\`. For best results, use the \`fetch\` that is passed to your \`load\` function: https://kit.svelte.dev/docs/loading#input-fetch` + `Loading http://localhost:${port}/load/window-fetch/data.json using \`window.fetch\`. For best results, use the \`fetch\` that is passed to your \`load\` function: https://kit.svelte.dev/docs/load#input-fetch` ); }); } @@ -482,7 +469,7 @@ test.describe('Prefetching', () => { // svelte request made is environment dependent if (process.env.DEV) { - expect(requests.filter((req) => req.endsWith('index.svelte')).length).toBe(1); + expect(requests.filter((req) => req.endsWith('+page.svelte')).length).toBe(1); } else { // the preload helper causes an additional request to be made in Firefox, // so we use toBeGreaterThan rather than toBe @@ -536,7 +523,7 @@ test.describe('Routing', () => { test('navigates to a new page without reloading', async ({ app, page, clicknav }) => { await page.goto('/routing'); - await app.prefetchRoutes(['/routing/a']).catch((e) => { + await app.prefetch('/routing/a').catch((e) => { // from error handler tests; ignore if (!e.message.includes('Crashing now')) throw e; }); @@ -595,7 +582,7 @@ test.describe('Shadow DOM', () => { test('client router captures anchors in shadow dom', async ({ app, page, clicknav }) => { await page.goto('/routing/shadow-dom'); - await app.prefetchRoutes(['/routing/a']).catch((e) => { + await app.prefetch('/routing/a').catch((e) => { // from error handler tests; ignore if (!e.message.includes('Crashing now')) throw e; }); @@ -610,3 +597,20 @@ test.describe('Shadow DOM', () => { expect(requests).toEqual([]); }); }); + +test.describe('Page Store', () => { + test('Updates data if changed even when no URL change visible', async ({ page }) => { + await page.goto('/store/data/only-data-changes'); + expect(await page.textContent('#page-data')).toBe('{"answer":42,"calls":0}'); + expect(await page.textContent('#store-data')).toBe( + '{"foo":{"bar":"Custom layout"},"name":"SvelteKit","value":123,"answer":42,"calls":0}' + ); + + await page.click('button'); + + expect(await page.textContent('#page-data')).toBe('{"answer":1337}'); + expect(await page.textContent('#store-data')).toBe( + '{"foo":{"bar":"Custom layout"},"name":"SvelteKit","value":123,"answer":1337}' + ); + }); +}); diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js index 077724b3d1e1..e54dfb7eb681 100644 --- a/packages/kit/test/apps/basics/test/server.test.js +++ b/packages/kit/test/apps/basics/test/server.test.js @@ -1,5 +1,5 @@ import { expect } from '@playwright/test'; -import { start_server, test } from '../../../utils.js'; +import { test } from '../../../utils.js'; import { createHash, randomBytes } from 'node:crypto'; /** @typedef {import('@playwright/test').Response} Response */ @@ -13,88 +13,6 @@ test.describe('Caching', () => { const response = await request.get('/caching'); expect(response.headers()['cache-control']).toBe('public, max-age=30'); }); - - test('sets cache-control: private if page uses session in load and cache.private is unset', async ({ - request - }) => { - const response = await request.get('/caching/private/uses-session-in-load'); - expect(response.headers()['cache-control']).toBe('private, max-age=30'); - }); - - test('sets cache-control: private if page uses session in init and cache.private is unset', async ({ - request - }) => { - const response = await request.get('/caching/private/uses-session-in-init'); - expect(response.headers()['cache-control']).toBe('private, max-age=30'); - }); - - test('sets cache-control: private even if page doesnt use session but one exists and cache.private is unset', async ({ - request - }) => { - const response = await request.get('/caching/private/has-session'); - expect(response.headers()['cache-control']).toBe('private, max-age=30'); - }); - - test('sets cache-control: private if page uses fetch and cache.private is unset', async ({ - request - }) => { - const response = await request.get('/caching/private/uses-fetch?credentials=include'); - expect(response.headers()['cache-control']).toBe('private, max-age=30'); - }); - - test('sets cache-control: public if page uses fetch without credentials and cache.private is unset', async ({ - request - }) => { - const response = await request.get('/caching/private/uses-fetch?credentials=omit'); - expect(response.headers()['cache-control']).toBe('public, max-age=30'); - }); - - test('sets cache-control: private if cache.private is true', async ({ request }) => { - const response = await request.get('/caching/private/uses-cache-private?private=true'); - expect(response.headers()['cache-control']).toBe('private, max-age=30'); - }); - - test('sets cache-control: public if cache.private is false', async ({ request }) => { - const response = await request.get('/caching/private/uses-cache-private?private=false'); - expect(response.headers()['cache-control']).toBe('public, max-age=30'); - }); - - test('sets cache-control: public if page uses session in load and cache.private is false', async ({ - request - }) => { - const response = await request.get('/caching/private/uses-session-in-load?private=false'); - expect(response.headers()['cache-control']).toBe('public, max-age=30'); - }); - - test('sets cache-control: public if page uses session in init and cache.private is false', async ({ - request - }) => { - const response = await request.get('/caching/private/uses-session-in-init?private=false'); - expect(response.headers()['cache-control']).toBe('public, max-age=30'); - }); - - test('sets cache-control: public if page has session and cache.private is false', async ({ - request - }) => { - const response = await request.get('/caching/private/has-session?private=false'); - expect(response.headers()['cache-control']).toBe('public, max-age=30'); - }); - - test('sets cache-control: public if page uses fetch and cache.private is false', async ({ - request - }) => { - const response = await request.get( - '/caching/private/uses-fetch?credentials=include&private=false' - ); - expect(response.headers()['cache-control']).toBe('public, max-age=30'); - }); - - test('sets cache-control: private if page uses fetch without credentials and cache.private is true', async ({ - request - }) => { - const response = await request.get('/caching/private/uses-fetch?credentials=omit&private=true'); - expect(response.headers()['cache-control']).toBe('private, max-age=30'); - }); }); test.describe('Content-Type', () => { @@ -104,78 +22,7 @@ test.describe('Content-Type', () => { }); }); -test.describe('ETags', () => { - test('generates etag/304 for text body', async ({ request }) => { - const r1 = await request.get('/etag/text'); - const etag = r1.headers()['etag']; - expect(etag).toBeTruthy(); - - const r2 = await request.get('/etag/text', { - headers: { - 'if-none-match': etag - } - }); - - expect(r2.status()).toBe(304); - expect(r2.headers()['expires']).toBe('yesterday'); - }); - - test('generates etag/304 for binary body', async ({ request }) => { - const r1 = await request.get('/etag/binary'); - const etag = r1.headers()['etag']; - expect(etag).toBeTruthy(); - - const r2 = await request.get('/etag/binary', { - headers: { - 'if-none-match': etag - } - }); - - expect(r2.status()).toBe(304); - }); - - test('support W/ etag prefix', async ({ request }) => { - const r1 = await request.get('/etag/text'); - const etag = r1.headers()['etag']; - expect(etag).toBeTruthy(); - - const r2 = await request.get('/etag/text', { - headers: { - 'if-none-match': `W/${etag}` - } - }); - - expect(r2.status()).toBe(304); - }); - - test('custom etag', async ({ request }) => { - const r1 = await request.get('/etag/custom'); - const etag = r1.headers()['etag']; - expect(etag).toBe('@1234@'); - - const r2 = await request.get('/etag/custom', { - headers: { - 'if-none-match': '@1234@' - } - }); - - expect(r2.status()).toBe(304); - }); -}); - test.describe('Endpoints', () => { - test('200 status on empty endpoint', async ({ request }) => { - const response = await request.get('/endpoint-output/empty'); - expect(/** @type {import('@playwright/test').APIResponse} */ (response).status()).toBe(200); - expect(await response.json()).toEqual({}); - }); - - test('set-cookie without body', async ({ request }) => { - const response = await request.get('/endpoint-output/headers'); - expect(/** @type {import('@playwright/test').APIResponse} */ (response).status()).toBe(200); - expect(response.headers()['set-cookie']).toBeDefined(); - }); - test('HEAD with matching headers but without body', async ({ request }) => { const url = '/endpoint-output/body'; @@ -202,63 +49,9 @@ test.describe('Endpoints', () => { expect(headers.head).toEqual(headers.get); }); - test('200 status by default', async ({ request }) => { - const response = await request.get('/endpoint-output/body'); - expect(/** @type {import('@playwright/test').APIResponse} */ (response).status()).toBe(200); - expect(await response.text()).toBe('{}'); - }); - - // TODO are these tests useful? - test('always returns a body', async ({ request }) => { - const response = await request.get('/endpoint-output/empty'); - expect(typeof (await response.body())).toEqual('object'); - }); - - test('null body returns null json value', async ({ request }) => { - const response = await request.get('/endpoint-output/null'); - expect(/** @type {import('@playwright/test').APIResponse} */ (response).status()).toBe(200); - expect(await response.json()).toBe(null); - }); - - test('gets string response with XML Content-Type', async ({ request }) => { - const response = await request.get('/endpoint-output/xml-text'); - - expect(response.headers()['content-type']).toBe('application/xml'); - expect(await response.text()).toBe(''); - }); - - test('gets binary response with XML Content-Type', async ({ request }) => { - const response = await request.get('/endpoint-output/xml-bytes'); - - expect(response.headers()['content-type']).toBe('application/xml'); - expect(await response.text()).toBe(''); - }); - - test('allows headers to be a Headers object', async ({ request }) => { - const response = await request.get('/endpoint-output/headers-object'); - - expect(response.headers()['x-foo']).toBe('bar'); - }); - - test('allows return value to be a Response', async ({ request }) => { - const { port, close } = await start_server((req, res) => { - res.writeHead(200, { - 'X-Foo': 'bar' - }); - - res.end('ok'); - }); - - try { - const response = await request.get(`/endpoint-output/proxy?port=${port}`); - - expect(await response.text()).toBe('ok'); - expect(response.headers()['x-foo']).toBe('bar'); - } finally { - await close(); - } - }); - + // TODO all the remaining tests in this section are really only testing + // setResponse, since we're not otherwise changing anything on the response. + // might be worth making these unit tests instead test('multiple set-cookie on endpoints using GET', async ({ request }) => { const response = await request.get('/set-cookie'); @@ -274,17 +67,7 @@ test.describe('Endpoints', () => { ]); }); - test('Standalone endpoint is not accessible via /__data.json suffix', async ({ request }) => { - const r1 = await request.get('/endpoint-output/simple', { - headers: { accept: 'application/json' } - }); - - expect(await r1.json()).toEqual({ answer: 42 }); - - const r2 = await request.get('/endpoint-output/simple/__data.json'); - expect(r2.status()).toBe(404); - }); - + // TODO see above test('body can be a binary ReadableStream', async ({ request }) => { const interruptedResponse = request.get('/endpoint-output/stream-throw-error'); await expect(interruptedResponse).rejects.toThrow('socket hang up'); @@ -295,6 +78,7 @@ test.describe('Endpoints', () => { expect(response.headers()['digest']).toEqual(`sha-256=${digest}`); }); + // TODO see above test('stream can be canceled with TypeError', async ({ request }) => { const responseBefore = await request.get('/endpoint-output/stream-typeerror?what'); expect(await responseBefore.text()).toEqual('null'); @@ -306,6 +90,7 @@ test.describe('Endpoints', () => { expect(await responseAfter.text()).toEqual('TypeError'); }); + // TODO see above test('request body can be read slow', async ({ request }) => { const data = randomBytes(1024 * 256); const digest = createHash('sha256').update(data).digest('base64url'); @@ -319,12 +104,11 @@ test.describe('Errors', () => { const response = await request.get('/errors/invalid-route-response'); expect(/** @type {import('@playwright/test').APIResponse} */ (response).status()).toBe(500); - expect(await response.text()).toMatch('expected an object'); + expect(await response.text()).toMatch( + 'Invalid response from route /errors/invalid-route-response: handler should return a Response object' + ); }); - // TODO before we implemented route fallthroughs, and there was a 1:1 - // regex:route relationship, it was simple to say 'method not implemented - // for this endpoint'. now it's a little tricker. does a 404 suffice? test('unhandled http method', async ({ request }) => { const response = await request.put('/errors/invalid-route-response'); @@ -368,17 +152,10 @@ test.describe('Errors', () => { } }); - const { message, name, stack } = await response.json(); + const { message } = await response.json(); expect(response.status()).toBe(400); - expect(name).toBe('FancyError'); expect(message).toBe('oops'); - - if (process.env.DEV) { - expect(stack.split('\n').length).toBeGreaterThan(1); - } else { - expect(stack.split('\n').length).toBe(1); - } }); test('returns 400 when accessing a malformed URI', async ({ page }) => { @@ -463,7 +240,10 @@ test.describe('Shadowed pages', () => { delete headers.get[name]; }); - expect(headers.head).toEqual(headers.get); + expect(headers.get).toEqual({ + ...headers.head, + 'content-type': 'application/json' + }); }); test('Responds from endpoint if Accept includes application/json but not text/html', async ({ diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index 83a4c5b780b3..cb624a0ef504 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -320,9 +320,11 @@ test.describe('Encoded paths', () => { expect(await page.innerText('pre')).toBe('/苗条?foo=bar&fizz=buzz'); }); - test('sets charset on JSON Content-Type', async ({ request }) => { + test('serializes JSON correctly', async ({ request }) => { const response = await request.get('/encoded/endpoint'); - expect(response.headers()['content-type']).toBe('application/json; charset=utf-8'); + expect(await response.json()).toEqual({ + fruit: '🍎🍇🍌' + }); }); test('allows %-encoded characters in directory names', async ({ page, clicknav }) => { @@ -429,16 +431,6 @@ test.describe('Errors', () => { expect(/** @type {Response} */ (response).status()).toBe(555); }); - test('server-side error from load() is malformed', async ({ page }) => { - await page.goto('/errors/load-error-malformed-server'); - - const body = await page.textContent('body'); - - expect(body).toMatch( - 'Error: "error" property returned from load() must be a string or instance of Error, received type "object"' - ); - }); - test('error in endpoint', async ({ page, read_errors }) => { const res = await page.goto('/errors/endpoint'); @@ -451,28 +443,10 @@ test.describe('Errors', () => { } expect(res && res.status()).toBe(500); - expect(await page.textContent('#message')).toBe('This is your custom error page saying: ""'); - - const contents = await page.textContent('#stack'); - const location = /endpoint\.svelte:12:9|endpoint\.svelte:12:15/; // TODO: Remove second location with Vite 2.9 - - if (process.env.DEV) { - expect(contents).toMatch(location); - } else { - expect(contents).not.toMatch(location); - } - }); - - test('not ok response from endpoint', async ({ page, read_errors }) => { - const res = await page.goto('/errors/endpoint-not-ok'); - - expect(read_errors('/errors/endpoint-not-ok.json')).toBeUndefined(); - - expect(res && res.status()).toBe(555); - expect(await page.textContent('#message')).toBe('This is your custom error page saying: ""'); + expect(await page.textContent('#message')).toBe('This is your custom error page saying: "500"'); const contents = await page.textContent('#stack'); - const location = /endpoint-not-ok\.svelte:12:9|endpoint-not-ok\.svelte:12:15/; // TODO: Remove second location with Vite 2.9 + const location = '+page.js:7:9'; if (process.env.DEV) { expect(contents).toMatch(location); @@ -482,6 +456,8 @@ test.describe('Errors', () => { }); test('error in shadow endpoint', async ({ page, read_errors }) => { + const location = '+page.server.js:3:8'; + const res = await page.goto('/errors/endpoint-shadow'); // should include stack trace @@ -489,7 +465,7 @@ test.describe('Errors', () => { expect(lines[0]).toMatch('nope'); if (process.env.DEV) { - expect(lines[1]).toMatch('endpoint-shadow.js:3:8'); + expect(lines[1]).toMatch(location); } expect(res && res.status()).toBe(500); @@ -498,7 +474,6 @@ test.describe('Errors', () => { ); const contents = await page.textContent('#stack'); - const location = 'endpoint-shadow.js:3:8'; if (process.env.DEV) { expect(contents).toMatch(location); @@ -514,18 +489,10 @@ test.describe('Errors', () => { expect(res && res.status()).toBe(555); expect(await page.textContent('#message')).toBe( - 'This is your custom error page saying: "Failed to load data"' + 'This is your custom error page saying: "Error: 555"' ); }); - test('server-side 4xx status without error from load()', async ({ page }) => { - const response = await page.goto('/errors/load-status-without-error-server'); - - expect(await page.textContent('footer')).toBe('Custom layout'); - expect(await page.textContent('#message')).toBe('This is your custom error page saying: "401"'); - expect(/** @type {Response} */ (response).status()).toBe(401); - }); - test('error thrown in handle results in a rendered error page', async ({ page }) => { await page.goto('/errors/error-in-handle'); @@ -549,8 +516,10 @@ test.describe('Errors', () => { page }) => { await page.goto('/prerendering/mutative-endpoint'); - expect(await page.textContent('h1')).toBe( - '500: Cannot prerender pages that have endpoints with mutative methods' + expect(await page.textContent('h1')).toBe('500'); + + expect(await page.textContent('#message')).toBe( + 'This is your custom error page saying: "Cannot prerender pages that have endpoints with mutative methods"' ); }); @@ -572,14 +541,14 @@ test.describe('Errors', () => { if (process.env.DEV) { const lines = stack.split('\n'); - expect(lines[1]).toContain('get-implicit.js:4:8'); + expect(lines[1]).toContain('+page.server.js:4:8'); } const error = read_errors('/errors/page-endpoint/get-implicit'); expect(error).toContain('oops'); }); - test('page endpoint GET returned error message is preserved', async ({ + test('page endpoint GET HttpError message is preserved', async ({ page, clicknav, read_errors @@ -588,23 +557,20 @@ test.describe('Errors', () => { await clicknav('#get-explicit'); const json = await page.textContent('pre'); if (!json) throw new Error('Could not extract content from element'); - const { status, name, message, stack, fancy } = JSON.parse(json); + const { status, message, stack } = JSON.parse(json); expect(status).toBe(400); - expect(name).toBe('FancyError'); expect(message).toBe('oops'); - expect(fancy).toBe(true); - - if (process.env.DEV) { - const lines = stack.split('\n'); - expect(lines[1]).toContain('get-explicit.js:5:8'); - } + expect(stack).toBeUndefined(); const error = read_errors('/errors/page-endpoint/get-explicit'); expect(error).toBe(undefined); }); - test('page endpoint POST thrown error message is preserved', async ({ page, read_errors }) => { + test('page endpoint POST unexpected error message is preserved', async ({ + page, + read_errors + }) => { // The case where we're submitting a POST request via a form. // It should show the __error template with our message. await page.goto('/errors/page-endpoint'); @@ -620,31 +586,25 @@ test.describe('Errors', () => { if (process.env.DEV) { const lines = stack.split('\n'); - expect(lines[1]).toContain('post-implicit.js:4:8'); + expect(lines[1]).toContain('+page.server.js:4:8'); } const error = read_errors('/errors/page-endpoint/post-implicit'); expect(error).toContain('oops'); }); - test('page endpoint POST returned error message is preserved', async ({ page, read_errors }) => { + test('page endpoint POST HttpError error message is preserved', async ({ page, read_errors }) => { // The case where we're submitting a POST request via a form. // It should show the __error template with our message. await page.goto('/errors/page-endpoint'); await Promise.all([page.waitForNavigation(), page.click('#post-explicit')]); const json = await page.textContent('pre'); if (!json) throw new Error('Could not extract content from element'); - const { status, name, message, stack, fancy } = JSON.parse(json); + const { status, message, stack } = JSON.parse(json); expect(status).toBe(400); - expect(name).toBe('FancyError'); expect(message).toBe('oops'); - expect(fancy).toBe(true); - - if (process.env.DEV) { - const lines = stack.split('\n'); - expect(lines[1]).toContain('post-explicit.js:5:8'); - } + expect(stack).toBeUndefined(); const error = read_errors('/errors/page-endpoint/post-explicit'); expect(error).toBe(undefined); @@ -681,7 +641,7 @@ test.describe('Load', () => { const script_contents = await page.innerHTML('script[sveltekit\\:data-type="data"]'); const payload = - '{"status":200,"statusText":"","headers":{"content-type":"application/json; charset=utf-8"},"body":"{\\"answer\\":42}"}'; + '{"status":200,"statusText":"","headers":{"content-type":"application/json"},"body":"{\\"answer\\":42}"}'; expect(script_contents).toBe(payload); } @@ -732,11 +692,13 @@ test.describe('Load', () => { expect(await page.textContent('h1')).toBe('static file'); }); - test('stuff is inherited', async ({ page, javaScriptEnabled, app }) => { - await page.goto('/load/stuff/a/b/c'); + test('data is inherited', async ({ page, javaScriptEnabled, app }) => { + await page.goto('/load/parent/a/b/c'); expect(await page.textContent('h1')).toBe('message: original + new'); expect(await page.textContent('pre')).toBe( JSON.stringify({ + foo: { bar: 'Custom layout' }, + message: 'original + new', x: 'a', y: 'b', z: 'c' @@ -744,11 +706,13 @@ test.describe('Load', () => { ); if (javaScriptEnabled) { - await app.goto('/load/stuff/d/e/f'); + await app.goto('/load/parent/d/e/f'); expect(await page.textContent('h1')).toBe('message: original + new'); expect(await page.textContent('pre')).toBe( JSON.stringify({ + foo: { bar: 'Custom layout' }, + message: 'original + new', x: 'd', y: 'e', z: 'f' @@ -851,11 +815,11 @@ test.describe('Load', () => { test('does not leak props to other pages', async ({ page, clicknav }) => { await page.goto('/load/props/about'); - expect(await page.textContent('p')).toBe('Data: undefined'); + expect(await page.textContent('p')).toBe('Data: null'); await clicknav('[href="/load/props/"]'); expect(await page.textContent('p')).toBe('Data: Hello from Index!'); await clicknav('[href="/load/props/about"]'); - expect(await page.textContent('p')).toBe('Data: undefined'); + expect(await page.textContent('p')).toBe('Data: null'); }); test('server-side fetch respects set-cookie header', async ({ page, context }) => { @@ -1109,48 +1073,55 @@ test.describe('$app/stores', () => { expect(oops).toBeUndefined(); }); - test('page store contains stuff', async ({ page, clicknav }) => { - await page.goto('/store/stuff/www'); + test('page store contains data', async ({ page, clicknav }) => { + await page.goto('/store/data/www'); - expect(await page.textContent('#store-stuff')).toBe( - JSON.stringify({ name: 'SvelteKit', value: 456, page: 'www' }) + const foo = { bar: 'Custom layout' }; + + expect(await page.textContent('#store-data')).toBe( + JSON.stringify({ foo, name: 'SvelteKit', value: 456, page: 'www' }) ); - await clicknav('a[href="/store/stuff/zzz"]'); - expect(await page.textContent('#store-stuff')).toBe( - JSON.stringify({ name: 'SvelteKit', value: 456, page: 'zzz' }) + await clicknav('a[href="/store/data/zzz"]'); + expect(await page.textContent('#store-data')).toBe( + JSON.stringify({ foo, name: 'SvelteKit', value: 456, page: 'zzz' }) ); - await clicknav('a[href="/store/stuff/xxx"]'); - expect(await page.textContent('#store-stuff')).toBe( - JSON.stringify({ name: 'SvelteKit', value: 789, error: 'Params = xxx' }) + await clicknav('a[href="/store/data/xxx"]'); + expect(await page.textContent('#store-data')).toBe( + JSON.stringify({ foo, name: 'SvelteKit', value: 123 }) ); + expect(await page.textContent('#store-error')).toBe('Params = xxx'); - await clicknav('a[href="/store/stuff/yyy"]'); - expect(await page.textContent('#store-stuff')).toBe( - JSON.stringify({ name: 'SvelteKit', value: 789, error: 'Params = yyy' }) + await clicknav('a[href="/store/data/yyy"]'); + expect(await page.textContent('#store-data')).toBe( + JSON.stringify({ foo, name: 'SvelteKit', value: 123 }) ); + expect(await page.textContent('#store-error')).toBe('Params = yyy'); }); - test('should load stuff after reloading by goto', async ({ + test('should load data after reloading by goto', async ({ page, clicknav, javaScriptEnabled }) => { - await page.goto('/store/stuff/foo?reset=true'); - const stuff1 = JSON.stringify({ name: 'SvelteKit', value: 789, error: 'uh oh' }); - const stuff2 = JSON.stringify({ name: 'SvelteKit', value: 123, foo: true }); - await page.goto('/store/stuff/www'); + await page.goto('/store/data/foo?reset=true'); + const stuff1 = { foo: { bar: 'Custom layout' }, name: 'SvelteKit', value: 123 }; + const stuff2 = { ...stuff1, foo: true, number: 2 }; + const stuff3 = { ...stuff2 }; + await page.goto('/store/data/www'); - await clicknav('a[href="/store/stuff/foo"]'); - expect(await page.textContent('#store-stuff')).toBe(stuff1); + await clicknav('a[href="/store/data/foo"]'); + expect(JSON.parse(await page.textContent('#store-data'))).toEqual(stuff1); await clicknav('#reload-button'); - expect(await page.textContent('#store-stuff')).toBe(javaScriptEnabled ? stuff2 : stuff1); + expect(JSON.parse(await page.textContent('#store-data'))).toEqual( + javaScriptEnabled ? stuff2 : stuff1 + ); - await clicknav('a[href="/store/stuff/zzz"]'); - await clicknav('a[href="/store/stuff/foo"]'); - expect(await page.textContent('#store-stuff')).toBe(stuff2); + await clicknav('a[href="/store/data/zzz"]'); + await clicknav('a[href="/store/data/foo"]'); + expect(JSON.parse(await page.textContent('#store-data'))).toEqual(stuff3); }); test('navigating store contains from and to', async ({ app, page, javaScriptEnabled }) => { @@ -1286,15 +1257,13 @@ test.describe('Redirects', () => { expect(page.url()).toBe(`${baseURL}/redirect/missing-status/a`); expect(await page.textContent('h1')).toBe('500'); expect(await page.textContent('#message')).toBe( - 'This is your custom error page saying: ""redirect" property returned from load() must be accompanied by a 3xx status code"' + 'This is your custom error page saying: "Invalid status code"' ); if (!javaScriptEnabled) { // handleError is not invoked for client-side navigation const lines = read_errors('/redirect/missing-status/a').split('\n'); - expect(lines[0]).toBe( - 'Error: "redirect" property returned from load() must be accompanied by a 3xx status code' - ); + expect(lines[0]).toBe('Error: Invalid status code'); } }); @@ -1306,7 +1275,7 @@ test.describe('Redirects', () => { expect(page.url()).toBe(`${baseURL}/redirect/missing-status/b`); expect(await page.textContent('h1')).toBe('500'); expect(await page.textContent('#message')).toBe( - 'This is your custom error page saying: ""redirect" property returned from load() must be accompanied by a 3xx status code"' + 'This is your custom error page saying: "Invalid status code"' ); }); diff --git a/packages/kit/test/apps/basics/vite.config.js b/packages/kit/test/apps/basics/vite.config.js index 37431aed85a2..1938615ca91c 100644 --- a/packages/kit/test/apps/basics/vite.config.js +++ b/packages/kit/test/apps/basics/vite.config.js @@ -1,5 +1,5 @@ import * as path from 'path'; -import { plugin } from '../../utils.js'; +import { sveltekit } from '@sveltejs/kit/vite'; /** @type {import('vite').UserConfig} */ const config = { @@ -12,7 +12,7 @@ const config = { // the reload confuses Playwright include: ['cookie', 'marked'] }, - plugins: [plugin()], + plugins: [sveltekit()], server: { fs: { allow: [path.resolve('../../../src')] diff --git a/packages/kit/test/apps/options-2/package.json b/packages/kit/test/apps/options-2/package.json index e320a400103b..096eca6c80a7 100644 --- a/packages/kit/test/apps/options-2/package.json +++ b/packages/kit/test/apps/options-2/package.json @@ -6,7 +6,7 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "check": "node ../../cli.js sync && tsc && svelte-check", + "check": "svelte-kit sync && tsc && svelte-check", "test": "npm run test:dev && npm run test:build", "test:dev": "cross-env DEV=true playwright test", "test:build": "playwright test" diff --git a/packages/kit/test/apps/options-2/src/routes/index.svelte b/packages/kit/test/apps/options-2/src/routes/+page.svelte similarity index 100% rename from packages/kit/test/apps/options-2/src/routes/index.svelte rename to packages/kit/test/apps/options-2/src/routes/+page.svelte diff --git a/packages/kit/test/apps/options-2/vite.config.js b/packages/kit/test/apps/options-2/vite.config.js index 7d997f333156..3c59318ad7c6 100644 --- a/packages/kit/test/apps/options-2/vite.config.js +++ b/packages/kit/test/apps/options-2/vite.config.js @@ -1,5 +1,5 @@ import * as path from 'path'; -import { plugin } from '../../utils.js'; +import { sveltekit } from '@sveltejs/kit/vite'; /** @type {import('vite').UserConfig} */ const config = { @@ -7,7 +7,7 @@ const config = { minify: false }, clearScreen: false, - plugins: [plugin()], + plugins: [sveltekit()], server: { fs: { allow: [path.resolve('../../../src')] diff --git a/packages/kit/test/apps/options/package.json b/packages/kit/test/apps/options/package.json index 7b2ce7d155ba..5da7c11fc3b9 100644 --- a/packages/kit/test/apps/options/package.json +++ b/packages/kit/test/apps/options/package.json @@ -6,7 +6,7 @@ "dev": "vite dev -c vite.custom.config.js", "build": "vite build -c vite.custom.config.js --mode custom", "preview": "vite preview -c vite.custom.config.js", - "check": "node ../../cli.js sync && tsc && svelte-check", + "check": "svelte-kit sync && tsc && svelte-check", "test": "npm run test:dev && npm run test:build", "test:dev": "cross-env DEV=true playwright test", "test:build": "playwright test" diff --git a/packages/kit/test/apps/options/source/app.d.ts b/packages/kit/test/apps/options/source/app.d.ts index d8c081b14d05..71207691df42 100644 --- a/packages/kit/test/apps/options/source/app.d.ts +++ b/packages/kit/test/apps/options/source/app.d.ts @@ -6,6 +6,4 @@ declare namespace App { interface Platform {} interface Session {} - - interface Stuff {} } diff --git a/packages/kit/test/apps/options/source/pages/__layout.svelte b/packages/kit/test/apps/options/source/pages/+layout.svelte similarity index 100% rename from packages/kit/test/apps/options/source/pages/__layout.svelte rename to packages/kit/test/apps/options/source/pages/+layout.svelte diff --git a/packages/kit/test/apps/options/source/pages/index.svelte b/packages/kit/test/apps/options/source/pages/+page.svelte similarity index 100% rename from packages/kit/test/apps/options/source/pages/index.svelte rename to packages/kit/test/apps/options/source/pages/+page.svelte diff --git a/packages/kit/test/apps/options/source/pages/base/+page.js b/packages/kit/test/apps/options/source/pages/base/+page.js new file mode 100644 index 000000000000..fd56523450f6 --- /dev/null +++ b/packages/kit/test/apps/options/source/pages/base/+page.js @@ -0,0 +1,11 @@ +import { base, assets } from '$app/paths'; + +/** @type {import('@sveltejs/kit').Load} */ +export async function load() { + return { + paths: { + base, + assets + } + }; +} diff --git a/packages/kit/test/apps/options/source/pages/base/+page.svelte b/packages/kit/test/apps/options/source/pages/base/+page.svelte new file mode 100644 index 000000000000..0790bb9c17cb --- /dev/null +++ b/packages/kit/test/apps/options/source/pages/base/+page.svelte @@ -0,0 +1,25 @@ + + + + +

{data.paths.base}

+

{data.paths.assets}

+ + + +{#if n === 1} +

button has been clicked 1 time

+{/if} + + diff --git a/packages/kit/test/apps/options/source/pages/base/[slug].svelte b/packages/kit/test/apps/options/source/pages/base/[slug]/+page.svelte similarity index 100% rename from packages/kit/test/apps/options/source/pages/base/[slug].svelte rename to packages/kit/test/apps/options/source/pages/base/[slug]/+page.svelte diff --git a/packages/kit/test/apps/options/source/pages/base/index.svelte b/packages/kit/test/apps/options/source/pages/base/index.svelte deleted file mode 100644 index 08afa55450a7..000000000000 --- a/packages/kit/test/apps/options/source/pages/base/index.svelte +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - -

{data.base}

-

{data.assets}

- - - -{#if n === 1} -

button has been clicked 1 time

-{/if} - - diff --git a/packages/kit/test/apps/options/source/pages/csp/index.svelte b/packages/kit/test/apps/options/source/pages/csp/+page.svelte similarity index 100% rename from packages/kit/test/apps/options/source/pages/csp/index.svelte rename to packages/kit/test/apps/options/source/pages/csp/+page.svelte diff --git a/packages/kit/test/apps/options/source/pages/custom-extensions/__layout.svelte b/packages/kit/test/apps/options/source/pages/custom-extensions/+layout.svelte similarity index 100% rename from packages/kit/test/apps/options/source/pages/custom-extensions/__layout.svelte rename to packages/kit/test/apps/options/source/pages/custom-extensions/+layout.svelte diff --git a/packages/kit/test/apps/options/source/pages/custom-extensions/index.jesuslivesineveryone b/packages/kit/test/apps/options/source/pages/custom-extensions/+page.jesuslivesineveryone similarity index 100% rename from packages/kit/test/apps/options/source/pages/custom-extensions/index.jesuslivesineveryone rename to packages/kit/test/apps/options/source/pages/custom-extensions/+page.jesuslivesineveryone diff --git a/packages/kit/test/apps/options/source/pages/custom-extensions/[slug].svelte.md b/packages/kit/test/apps/options/source/pages/custom-extensions/[slug]/+page.svelte.md similarity index 100% rename from packages/kit/test/apps/options/source/pages/custom-extensions/[slug].svelte.md rename to packages/kit/test/apps/options/source/pages/custom-extensions/[slug]/+page.svelte.md diff --git a/packages/kit/test/apps/options/source/pages/custom-extensions/a.svelte b/packages/kit/test/apps/options/source/pages/custom-extensions/a/+page.svelte similarity index 100% rename from packages/kit/test/apps/options/source/pages/custom-extensions/a.svelte rename to packages/kit/test/apps/options/source/pages/custom-extensions/a/+page.svelte diff --git a/packages/kit/test/apps/options/source/pages/custom-extensions/const.whokilledthemuffinman b/packages/kit/test/apps/options/source/pages/custom-extensions/const/+page.whokilledthemuffinman similarity index 100% rename from packages/kit/test/apps/options/source/pages/custom-extensions/const.whokilledthemuffinman rename to packages/kit/test/apps/options/source/pages/custom-extensions/const/+page.whokilledthemuffinman diff --git a/packages/kit/test/apps/options/source/pages/custom-extensions/unsafe-replacement.svelte b/packages/kit/test/apps/options/source/pages/custom-extensions/unsafe-replacement/+page.svelte similarity index 100% rename from packages/kit/test/apps/options/source/pages/custom-extensions/unsafe-replacement.svelte rename to packages/kit/test/apps/options/source/pages/custom-extensions/unsafe-replacement/+page.svelte diff --git a/packages/kit/test/apps/options/source/pages/endpoint.js b/packages/kit/test/apps/options/source/pages/endpoint.js deleted file mode 100644 index 390c0832f5a6..000000000000 --- a/packages/kit/test/apps/options/source/pages/endpoint.js +++ /dev/null @@ -1,5 +0,0 @@ -export function GET() { - return { - body: 'hi' - }; -} diff --git a/packages/kit/test/apps/options/source/pages/endpoint/+server.js b/packages/kit/test/apps/options/source/pages/endpoint/+server.js new file mode 100644 index 000000000000..c92d38fed506 --- /dev/null +++ b/packages/kit/test/apps/options/source/pages/endpoint/+server.js @@ -0,0 +1,3 @@ +export function GET() { + return new Response('hi'); +} diff --git a/packages/kit/test/apps/options/source/pages/error.svelte b/packages/kit/test/apps/options/source/pages/error/+page.svelte similarity index 100% rename from packages/kit/test/apps/options/source/pages/error.svelte rename to packages/kit/test/apps/options/source/pages/error/+page.svelte diff --git a/packages/kit/test/apps/options/source/pages/mode/+page.server.js b/packages/kit/test/apps/options/source/pages/mode/+page.server.js new file mode 100644 index 000000000000..e800b740e64d --- /dev/null +++ b/packages/kit/test/apps/options/source/pages/mode/+page.server.js @@ -0,0 +1,5 @@ +export function load() { + return { + mode_from_endpoint: import.meta.env.MODE + }; +} diff --git a/packages/kit/test/apps/options/source/pages/mode/+page.svelte b/packages/kit/test/apps/options/source/pages/mode/+page.svelte new file mode 100644 index 000000000000..466630884129 --- /dev/null +++ b/packages/kit/test/apps/options/source/pages/mode/+page.svelte @@ -0,0 +1,10 @@ + + +

+ {data.mode_from_endpoint} === {import.meta.env.MODE} === {dev ? 'development' : 'custom'} +

diff --git a/packages/kit/test/apps/options/source/pages/mode/index.js b/packages/kit/test/apps/options/source/pages/mode/index.js deleted file mode 100644 index 8ef254f8e6ff..000000000000 --- a/packages/kit/test/apps/options/source/pages/mode/index.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - mode_from_endpoint: import.meta.env.MODE - } - }; -} diff --git a/packages/kit/test/apps/options/source/pages/mode/index.svelte b/packages/kit/test/apps/options/source/pages/mode/index.svelte deleted file mode 100644 index 5b7950bd097b..000000000000 --- a/packages/kit/test/apps/options/source/pages/mode/index.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -

- {mode_from_endpoint} === {import.meta.env.MODE} === {dev ? 'development' : 'custom'} -

diff --git a/packages/kit/test/apps/options/source/pages/page-endpoint/+page.server.js b/packages/kit/test/apps/options/source/pages/page-endpoint/+page.server.js new file mode 100644 index 000000000000..f3c3d055efa5 --- /dev/null +++ b/packages/kit/test/apps/options/source/pages/page-endpoint/+page.server.js @@ -0,0 +1,5 @@ +export function load() { + return { + message: 'hi' + }; +} diff --git a/packages/kit/test/apps/options/source/pages/page-endpoint/+page.svelte b/packages/kit/test/apps/options/source/pages/page-endpoint/+page.svelte new file mode 100644 index 000000000000..30395e5ed42b --- /dev/null +++ b/packages/kit/test/apps/options/source/pages/page-endpoint/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.message}

diff --git a/packages/kit/test/apps/options/source/pages/page-endpoint/index.js b/packages/kit/test/apps/options/source/pages/page-endpoint/index.js deleted file mode 100644 index bab1f222079c..000000000000 --- a/packages/kit/test/apps/options/source/pages/page-endpoint/index.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - data: 'hi' - } - }; -} diff --git a/packages/kit/test/apps/options/source/pages/page-endpoint/index.svelte b/packages/kit/test/apps/options/source/pages/page-endpoint/index.svelte deleted file mode 100644 index 173c865c665d..000000000000 --- a/packages/kit/test/apps/options/source/pages/page-endpoint/index.svelte +++ /dev/null @@ -1,6 +0,0 @@ - - -

{data}

diff --git a/packages/kit/test/apps/options/source/pages/prefetching/index.svelte b/packages/kit/test/apps/options/source/pages/prefetching/+page.svelte similarity index 100% rename from packages/kit/test/apps/options/source/pages/prefetching/index.svelte rename to packages/kit/test/apps/options/source/pages/prefetching/+page.svelte diff --git a/packages/kit/test/apps/options/source/pages/prefetching/prefetched.js b/packages/kit/test/apps/options/source/pages/prefetching/prefetched.js deleted file mode 100644 index d51f5fd3c271..000000000000 --- a/packages/kit/test/apps/options/source/pages/prefetching/prefetched.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - message: 'prefetched' - } - }; -} diff --git a/packages/kit/test/apps/options/source/pages/prefetching/prefetched.svelte b/packages/kit/test/apps/options/source/pages/prefetching/prefetched.svelte deleted file mode 100644 index 7c991db4005a..000000000000 --- a/packages/kit/test/apps/options/source/pages/prefetching/prefetched.svelte +++ /dev/null @@ -1,6 +0,0 @@ - - -

{message}

diff --git a/packages/kit/test/apps/options/source/pages/prefetching/prefetched/+page.server.js b/packages/kit/test/apps/options/source/pages/prefetching/prefetched/+page.server.js new file mode 100644 index 000000000000..085b3690a2c2 --- /dev/null +++ b/packages/kit/test/apps/options/source/pages/prefetching/prefetched/+page.server.js @@ -0,0 +1,5 @@ +export function load() { + return { + message: 'prefetched' + }; +} diff --git a/packages/kit/test/apps/options/source/pages/prefetching/prefetched/+page.svelte b/packages/kit/test/apps/options/source/pages/prefetching/prefetched/+page.svelte new file mode 100644 index 000000000000..65b84bb82fa9 --- /dev/null +++ b/packages/kit/test/apps/options/source/pages/prefetching/prefetched/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.message}

diff --git a/packages/kit/test/apps/options/source/pages/slash/index.svelte b/packages/kit/test/apps/options/source/pages/slash/+page.svelte similarity index 100% rename from packages/kit/test/apps/options/source/pages/slash/index.svelte rename to packages/kit/test/apps/options/source/pages/slash/+page.svelte diff --git a/packages/kit/test/apps/options/source/pages/slash/child.svelte b/packages/kit/test/apps/options/source/pages/slash/child/+page.svelte similarity index 100% rename from packages/kit/test/apps/options/source/pages/slash/child.svelte rename to packages/kit/test/apps/options/source/pages/slash/child/+page.svelte diff --git a/packages/kit/test/apps/options/test/test.js b/packages/kit/test/apps/options/test/test.js index bf94a72c9215..dff361ae6b54 100644 --- a/packages/kit/test/apps/options/test/test.js +++ b/packages/kit/test/apps/options/test/test.js @@ -159,7 +159,10 @@ test.describe('trailingSlash', () => { test('can fetch data from page-endpoint', async ({ request, baseURL }) => { const r = await request.get('/path-base/page-endpoint/__data.json'); expect(r.url()).toBe(`${baseURL}/path-base/page-endpoint/__data.json`); - expect(await r.json()).toEqual({ data: 'hi' }); + expect(await r.json()).toEqual({ + type: 'data', + nodes: [{ data: null }, { data: { message: 'hi' } }] + }); }); test('accounts for trailingSlash when prefetching', async ({ diff --git a/packages/kit/test/apps/options/vite.custom.config.js b/packages/kit/test/apps/options/vite.custom.config.js index 7d997f333156..3c59318ad7c6 100644 --- a/packages/kit/test/apps/options/vite.custom.config.js +++ b/packages/kit/test/apps/options/vite.custom.config.js @@ -1,5 +1,5 @@ import * as path from 'path'; -import { plugin } from '../../utils.js'; +import { sveltekit } from '@sveltejs/kit/vite'; /** @type {import('vite').UserConfig} */ const config = { @@ -7,7 +7,7 @@ const config = { minify: false }, clearScreen: false, - plugins: [plugin()], + plugins: [sveltekit()], server: { fs: { allow: [path.resolve('../../../src')] diff --git a/packages/kit/test/apps/writes/package.json b/packages/kit/test/apps/writes/package.json index a126ef3427b4..396e62815efe 100644 --- a/packages/kit/test/apps/writes/package.json +++ b/packages/kit/test/apps/writes/package.json @@ -6,7 +6,7 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "check": "node ../../cli.js sync && tsc && svelte-check", + "check": "svelte-kit sync && tsc && svelte-check", "test": "npm run test:dev && npm run test:build", "test:dev": "rimraf test/errors.json && cross-env DEV=true playwright test", "test:build": "rimraf test/errors.json && playwright test" diff --git a/packages/kit/test/apps/writes/src/app.d.ts b/packages/kit/test/apps/writes/src/app.d.ts index d8c081b14d05..71207691df42 100644 --- a/packages/kit/test/apps/writes/src/app.d.ts +++ b/packages/kit/test/apps/writes/src/app.d.ts @@ -6,6 +6,4 @@ declare namespace App { interface Platform {} interface Session {} - - interface Stuff {} } diff --git a/packages/kit/test/apps/writes/src/routes/double-mount/index.svelte b/packages/kit/test/apps/writes/src/routes/double-mount/+page.svelte similarity index 100% rename from packages/kit/test/apps/writes/src/routes/double-mount/index.svelte rename to packages/kit/test/apps/writes/src/routes/double-mount/+page.svelte diff --git a/packages/kit/test/apps/writes/src/routes/new-route/index.svelte b/packages/kit/test/apps/writes/src/routes/new-route/+page.svelte similarity index 100% rename from packages/kit/test/apps/writes/src/routes/new-route/index.svelte rename to packages/kit/test/apps/writes/src/routes/new-route/+page.svelte diff --git a/packages/kit/test/apps/writes/test/test.js b/packages/kit/test/apps/writes/test/test.js index 7999e7cd06da..64cbf22c797e 100644 --- a/packages/kit/test/apps/writes/test/test.js +++ b/packages/kit/test/apps/writes/test/test.js @@ -18,22 +18,24 @@ test.describe('Filesystem updates', () => { const route = 'zzzz' + Date.now(); const content = 'Hello new route'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); - const filePath = path.join(__dirname, `../src/routes/new-route/${route}.svelte`); + const filepath = path.join(__dirname, `../src/routes/new-route/${route}/+page.svelte`); + const dir = path.dirname(filepath); try { - fs.writeFileSync(filePath, `

${content}

`); + fs.mkdirSync(dir); + fs.writeFileSync(filepath, `

${content}

`); await page.waitForTimeout(500); // this is the rare time we actually need waitForTimeout; we have no visibility into whether the module graph has been invalidated await page.goto(`/new-route/${route}`); expect(await page.textContent('h1')).toBe(content); } finally { - fs.unlinkSync(filePath); + fs.rmSync(dir, { recursive: true }); } }); } test('Components are not double-mounted', async ({ page, javaScriptEnabled }) => { - const file = fileURLToPath(new URL('../src/routes/double-mount/index.svelte', import.meta.url)); + const file = fileURLToPath(new URL('../src/routes/double-mount/+page.svelte', import.meta.url)); const contents = fs.readFileSync(file, 'utf-8'); const mounted = javaScriptEnabled ? 1 : 0; diff --git a/packages/kit/test/apps/writes/vite.config.js b/packages/kit/test/apps/writes/vite.config.js index 7d997f333156..3c59318ad7c6 100644 --- a/packages/kit/test/apps/writes/vite.config.js +++ b/packages/kit/test/apps/writes/vite.config.js @@ -1,5 +1,5 @@ import * as path from 'path'; -import { plugin } from '../../utils.js'; +import { sveltekit } from '@sveltejs/kit/vite'; /** @type {import('vite').UserConfig} */ const config = { @@ -7,7 +7,7 @@ const config = { minify: false }, clearScreen: false, - plugins: [plugin()], + plugins: [sveltekit()], server: { fs: { allow: [path.resolve('../../../src')] diff --git a/packages/kit/test/cli.js b/packages/kit/test/cli.js deleted file mode 100644 index 2749509a930f..000000000000 --- a/packages/kit/test/cli.js +++ /dev/null @@ -1,10 +0,0 @@ -import { spawn } from 'child_process'; -import { fileURLToPath } from 'url'; - -const [node, , ...args] = process.argv; -const mode = process.env.CI ? 'dist' : 'src'; -const bin = fileURLToPath(new URL(`../${mode}/cli.js`, import.meta.url)); - -spawn(node, [bin, ...args], { - stdio: 'inherit' -}); diff --git a/packages/kit/test/prerendering/basics/package.json b/packages/kit/test/prerendering/basics/package.json index a234dd6cb850..3c0ad3cca129 100644 --- a/packages/kit/test/prerendering/basics/package.json +++ b/packages/kit/test/prerendering/basics/package.json @@ -6,7 +6,7 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "check": "node ../../cli.js sync && tsc && svelte-check", + "check": "svelte-kit sync && tsc && svelte-check", "test": "npm run build && uvu test" }, "devDependencies": { diff --git a/packages/kit/test/prerendering/basics/src/routes/+page.server.js b/packages/kit/test/prerendering/basics/src/routes/+page.server.js new file mode 100644 index 000000000000..f10d94d2ba04 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/+page.server.js @@ -0,0 +1,5 @@ +export function load() { + return { + message: 'hello' + }; +} diff --git a/packages/kit/test/prerendering/basics/src/routes/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/+page.svelte new file mode 100644 index 000000000000..65b84bb82fa9 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.message}

diff --git a/packages/kit/test/prerendering/basics/src/routes/accesses-session.svelte b/packages/kit/test/prerendering/basics/src/routes/accesses-session.svelte deleted file mode 100644 index 7ffe59f13564..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/accesses-session.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - -

I should never be prerendered.

diff --git a/packages/kit/test/prerendering/basics/src/routes/accesses-session/+page.js b/packages/kit/test/prerendering/basics/src/routes/accesses-session/+page.js new file mode 100644 index 000000000000..bf5652d89fae --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/accesses-session/+page.js @@ -0,0 +1,3 @@ +export const load = ({ session }) => { + return session; +}; diff --git a/packages/kit/test/prerendering/basics/src/routes/accesses-session/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/accesses-session/+page.svelte new file mode 100644 index 000000000000..bf15aa47df08 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/accesses-session/+page.svelte @@ -0,0 +1 @@ +

I should never be prerendered.

diff --git a/packages/kit/test/prerendering/basics/src/routes/encoding/[path].json.js b/packages/kit/test/prerendering/basics/src/routes/encoding/[path].json.js deleted file mode 100644 index ead90ccd629f..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/encoding/[path].json.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET({ params }) { - return { - body: { - path: params.path - } - }; -} diff --git a/packages/kit/test/prerendering/basics/src/routes/encoding/[path].json/+server.js b/packages/kit/test/prerendering/basics/src/routes/encoding/[path].json/+server.js new file mode 100644 index 000000000000..a20830e260dd --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/encoding/[path].json/+server.js @@ -0,0 +1,8 @@ +import { json } from '@sveltejs/kit'; + +/** @type {import('./$types').RequestHandler} */ +export function GET({ params }) { + return json({ + path: params.path + }); +} diff --git a/packages/kit/test/prerendering/basics/src/routes/encoding/[path].svelte b/packages/kit/test/prerendering/basics/src/routes/encoding/[path]/+page.svelte similarity index 100% rename from packages/kit/test/prerendering/basics/src/routes/encoding/[path].svelte rename to packages/kit/test/prerendering/basics/src/routes/encoding/[path]/+page.svelte diff --git a/packages/kit/test/prerendering/basics/src/routes/encoding/path with spaces.svelte b/packages/kit/test/prerendering/basics/src/routes/encoding/path with spaces.svelte deleted file mode 100644 index 7e692c11f4c6..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/encoding/path with spaces.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - - - -

{a.path}

-

{b.path}

- -dynamic path with spaces -dynamic path with encoded spaces diff --git a/packages/kit/test/prerendering/basics/src/routes/encoding/path with spaces/+page.js b/packages/kit/test/prerendering/basics/src/routes/encoding/path with spaces/+page.js new file mode 100644 index 000000000000..d0248b03c167 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/encoding/path with spaces/+page.js @@ -0,0 +1,10 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const a = await fetch('/encoding/path with spaces.json'); + const b = await fetch('/encoding/path%20with%20encoded%20spaces.json'); + + return { + a: await a.json(), + b: await b.json() + }; +} diff --git a/packages/kit/test/prerendering/basics/src/routes/encoding/path with spaces/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/encoding/path with spaces/+page.svelte new file mode 100644 index 000000000000..5525321762da --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/encoding/path with spaces/+page.svelte @@ -0,0 +1,10 @@ + + +

{data.a.path}

+

{data.b.path}

+ +dynamic path with spaces +dynamic path with encoded spaces diff --git a/packages/kit/test/prerendering/basics/src/routes/encoding/redirect.svelte b/packages/kit/test/prerendering/basics/src/routes/encoding/redirect.svelte deleted file mode 100644 index 159549b5da22..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/encoding/redirect.svelte +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/packages/kit/test/prerendering/basics/src/routes/encoding/redirect/+page.js b/packages/kit/test/prerendering/basics/src/routes/encoding/redirect/+page.js new file mode 100644 index 000000000000..d40db8a08882 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/encoding/redirect/+page.js @@ -0,0 +1,6 @@ +import { redirect } from '@sveltejs/kit'; + +/** @type {import('@sveltejs/kit').Load} */ +export function load() { + throw redirect(307, '/encoding/redirected%20path%20with%20encoded%20spaces'); +} diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/bar.svelte b/packages/kit/test/prerendering/basics/src/routes/encoding/redirect/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/bar.svelte rename to packages/kit/test/prerendering/basics/src/routes/encoding/redirect/+page.svelte diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-404.svelte b/packages/kit/test/prerendering/basics/src/routes/fetch-404.svelte deleted file mode 100644 index 023d8f90291e..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/fetch-404.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - - - -

status: {status}

diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-404/+page.js b/packages/kit/test/prerendering/basics/src/routes/fetch-404/+page.js new file mode 100644 index 000000000000..56d013d62144 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/fetch-404/+page.js @@ -0,0 +1,6 @@ +/** @type {import('./$types').PageLoad} */ +export async function load({ fetch }) { + const { status } = await fetch('/missing.json'); + + return { status }; +} diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-404/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/fetch-404/+page.svelte new file mode 100644 index 000000000000..80f6ef3624e9 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/fetch-404/+page.svelte @@ -0,0 +1,6 @@ + + +

status: {data.status}

diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered.json.js b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered.json.js deleted file mode 100644 index 3b6abbc781f5..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered.json.js +++ /dev/null @@ -1,5 +0,0 @@ -export async function GET() { - return { - body: { answer: 42 } - }; -} diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered.json/+server.js b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered.json/+server.js new file mode 100644 index 000000000000..82d3cb7b8345 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered.json/+server.js @@ -0,0 +1,5 @@ +import { json } from '@sveltejs/kit'; + +export async function GET() { + return json({ answer: 42 }); +} diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered.svelte b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered.svelte deleted file mode 100644 index ea6c07029907..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - - -

the answer is {answer}

diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered/+page.js b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered/+page.js new file mode 100644 index 000000000000..b884d47611dd --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered/+page.js @@ -0,0 +1,7 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const url = '/fetch-endpoint/buffered.json'; + const res = await fetch(url); + + return await res.json(); +} diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered/+page.svelte new file mode 100644 index 000000000000..c890e7840e38 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/buffered/+page.svelte @@ -0,0 +1,6 @@ + + +

the answer is {data.answer}

diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered.json.js b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered.json.js deleted file mode 100644 index 3b6abbc781f5..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered.json.js +++ /dev/null @@ -1,5 +0,0 @@ -export async function GET() { - return { - body: { answer: 42 } - }; -} diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered.json/+server.js b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered.json/+server.js new file mode 100644 index 000000000000..82d3cb7b8345 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered.json/+server.js @@ -0,0 +1,5 @@ +import { json } from '@sveltejs/kit'; + +export async function GET() { + return json({ answer: 42 }); +} diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered.svelte b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered.svelte deleted file mode 100644 index 949a0b4e4b23..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - - - -

content-type: {headers.get('content-type')}

diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered/+page.js b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered/+page.js new file mode 100644 index 000000000000..de4123c2aafe --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered/+page.js @@ -0,0 +1,9 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const url = '/fetch-endpoint/not-buffered.json'; + const res = await fetch(url); + + return { + headers: res.headers + }; +} diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered/+page.svelte new file mode 100644 index 000000000000..bf73bfc65b89 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/fetch-endpoint/not-buffered/+page.svelte @@ -0,0 +1,6 @@ + + +

content-type: {data.headers.get('content-type')}

diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-image.svelte b/packages/kit/test/prerendering/basics/src/routes/fetch-image/+page.svelte similarity index 100% rename from packages/kit/test/prerendering/basics/src/routes/fetch-image.svelte rename to packages/kit/test/prerendering/basics/src/routes/fetch-image/+page.svelte diff --git a/packages/kit/test/prerendering/basics/src/routes/fetch-image/[...slug]/index.js b/packages/kit/test/prerendering/basics/src/routes/fetch-image/[...slug]/+server.js similarity index 85% rename from packages/kit/test/prerendering/basics/src/routes/fetch-image/[...slug]/index.js rename to packages/kit/test/prerendering/basics/src/routes/fetch-image/[...slug]/+server.js index 83f52a7bc27e..6d3a67e8e53f 100644 --- a/packages/kit/test/prerendering/basics/src/routes/fetch-image/[...slug]/index.js +++ b/packages/kit/test/prerendering/basics/src/routes/fetch-image/[...slug]/+server.js @@ -6,11 +6,9 @@ export async function GET({ params }) { const file = fs.readFileSync(`./static/image.${extension}`); - return { - status: 200, + return new Response(file, { headers: { 'Content-Type': 'image/' + extension - }, - body: file - }; + } + }); } diff --git a/packages/kit/test/prerendering/basics/src/routes/index.js b/packages/kit/test/prerendering/basics/src/routes/index.js deleted file mode 100644 index 6fe9d378bc26..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/index.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - message: 'hello' - } - }; -} diff --git a/packages/kit/test/prerendering/basics/src/routes/index.svelte b/packages/kit/test/prerendering/basics/src/routes/index.svelte deleted file mode 100644 index 7c991db4005a..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/index.svelte +++ /dev/null @@ -1,6 +0,0 @@ - - -

{message}

diff --git a/packages/kit/test/prerendering/basics/src/routes/load-file-with-spaces.svelte b/packages/kit/test/prerendering/basics/src/routes/load-file-with-spaces.svelte deleted file mode 100644 index b0a3d165fac2..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/load-file-with-spaces.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - - - -

answer: {answer}

diff --git a/packages/kit/test/prerendering/basics/src/routes/load-file-with-spaces/+page.js b/packages/kit/test/prerendering/basics/src/routes/load-file-with-spaces/+page.js new file mode 100644 index 000000000000..8198a825b03b --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/load-file-with-spaces/+page.js @@ -0,0 +1,14 @@ +/** @type {import('@sveltejs/kit').Load} */ +export async function load({ fetch }) { + const r1 = await fetch('/file%20with%20spaces.json'); + const p1 = await r1.json(); + + const r2 = await fetch('/file with spaces.json'); + const p2 = await r2.json(); + + if (p1.answer !== p2.answer) { + throw new Error('oops'); + } + + return p1; +} diff --git a/packages/kit/test/prerendering/basics/src/routes/load-file-with-spaces/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/load-file-with-spaces/+page.svelte new file mode 100644 index 000000000000..d03049f91541 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/load-file-with-spaces/+page.svelte @@ -0,0 +1,6 @@ + + +

answer: {data.answer}

diff --git a/packages/kit/test/prerendering/basics/src/routes/max-age.svelte b/packages/kit/test/prerendering/basics/src/routes/max-age.svelte deleted file mode 100644 index d6053f889af5..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/max-age.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -

This page will be cached for 5 minutes

diff --git a/packages/kit/test/prerendering/basics/src/routes/max-age/+page.js b/packages/kit/test/prerendering/basics/src/routes/max-age/+page.js new file mode 100644 index 000000000000..53fbb561c0fd --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/max-age/+page.js @@ -0,0 +1,6 @@ +/** @type {import('@sveltejs/kit').Load} */ +export function load({ setHeaders }) { + setHeaders({ + 'cache-control': 'max-age=300' + }); +} diff --git a/packages/kit/test/prerendering/basics/src/routes/max-age/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/max-age/+page.svelte new file mode 100644 index 000000000000..df56b13d96fe --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/max-age/+page.svelte @@ -0,0 +1 @@ +

This page will be cached for 5 minutes

diff --git a/packages/kit/test/prerendering/basics/src/routes/origin/index.js b/packages/kit/test/prerendering/basics/src/routes/origin/+page.server.js similarity index 68% rename from packages/kit/test/prerendering/basics/src/routes/origin/index.js rename to packages/kit/test/prerendering/basics/src/routes/origin/+page.server.js index dfe4afa7cb96..984bab08bc55 100644 --- a/packages/kit/test/prerendering/basics/src/routes/origin/index.js +++ b/packages/kit/test/prerendering/basics/src/routes/origin/+page.server.js @@ -1,10 +1,8 @@ -export async function GET({ url }) { +export async function load({ url }) { const res = await fetch(new URL('/origin/message.json', url.origin).href); const { message } = await res.json(); return { - body: { - message - } + message }; } diff --git a/packages/kit/test/prerendering/basics/src/routes/origin/index.svelte b/packages/kit/test/prerendering/basics/src/routes/origin/+page.svelte similarity index 50% rename from packages/kit/test/prerendering/basics/src/routes/origin/index.svelte rename to packages/kit/test/prerendering/basics/src/routes/origin/+page.svelte index 5de1b110c2ee..eb8ba6ef08b9 100644 --- a/packages/kit/test/prerendering/basics/src/routes/origin/index.svelte +++ b/packages/kit/test/prerendering/basics/src/routes/origin/+page.svelte @@ -1,9 +1,9 @@ -

{message}

+

{data.message}

{$page.url.origin}

diff --git a/packages/kit/test/prerendering/basics/src/routes/origin/message.json.js b/packages/kit/test/prerendering/basics/src/routes/origin/message.json.js deleted file mode 100644 index 6fe9d378bc26..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/origin/message.json.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - message: 'hello' - } - }; -} diff --git a/packages/kit/test/prerendering/basics/src/routes/origin/message.json/+server.js b/packages/kit/test/prerendering/basics/src/routes/origin/message.json/+server.js new file mode 100644 index 000000000000..2e53051d87ab --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/origin/message.json/+server.js @@ -0,0 +1,7 @@ +import { json } from '@sveltejs/kit'; + +export function GET() { + return json({ + message: 'hello' + }); +} diff --git a/packages/kit/test/prerendering/basics/src/routes/prerendering-true.svelte b/packages/kit/test/prerendering/basics/src/routes/prerendering-true/+page.svelte similarity index 100% rename from packages/kit/test/prerendering/basics/src/routes/prerendering-true.svelte rename to packages/kit/test/prerendering/basics/src/routes/prerendering-true/+page.svelte diff --git a/packages/kit/test/prerendering/basics/src/routes/redirect-encoded.svelte b/packages/kit/test/prerendering/basics/src/routes/redirect-encoded.svelte deleted file mode 100644 index fe69f129afce..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/redirect-encoded.svelte +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/packages/kit/test/prerendering/basics/src/routes/redirect-encoded/+page.js b/packages/kit/test/prerendering/basics/src/routes/redirect-encoded/+page.js new file mode 100644 index 000000000000..9dfac2b31492 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/redirect-encoded/+page.js @@ -0,0 +1,9 @@ +import { redirect } from '@sveltejs/kit'; + +/** @type {import('@sveltejs/kit').Load} */ +export function load() { + throw redirect( + 301, + `https://example.com/redirected?returnTo=${encodeURIComponent('/foo?bar=baz')}` + ); +} diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[xx].svelte b/packages/kit/test/prerendering/basics/src/routes/redirect-encoded/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[xx].svelte rename to packages/kit/test/prerendering/basics/src/routes/redirect-encoded/+page.svelte diff --git a/packages/kit/test/prerendering/basics/src/routes/redirect-malicious.svelte b/packages/kit/test/prerendering/basics/src/routes/redirect-malicious.svelte deleted file mode 100644 index a62c94eadc9b..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/redirect-malicious.svelte +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/packages/kit/test/prerendering/basics/src/routes/redirect-malicious/+page.js b/packages/kit/test/prerendering/basics/src/routes/redirect-malicious/+page.js new file mode 100644 index 000000000000..6fd54b56dfda --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/redirect-malicious/+page.js @@ -0,0 +1,6 @@ +import { redirect } from '@sveltejs/kit'; + +/** @type {import('@sveltejs/kit').Load} */ +export function load() { + throw redirect(301, 'https://example.com/alert("pwned")'); +} diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].json.js b/packages/kit/test/prerendering/basics/src/routes/redirect-malicious/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].json.js rename to packages/kit/test/prerendering/basics/src/routes/redirect-malicious/+page.svelte diff --git a/packages/kit/test/prerendering/basics/src/routes/redirect.svelte b/packages/kit/test/prerendering/basics/src/routes/redirect.svelte deleted file mode 100644 index 85e2614d807b..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/redirect.svelte +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/packages/kit/test/prerendering/basics/src/routes/redirect/+page.js b/packages/kit/test/prerendering/basics/src/routes/redirect/+page.js new file mode 100644 index 000000000000..0ed002001e08 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/redirect/+page.js @@ -0,0 +1,6 @@ +import { redirect } from '@sveltejs/kit'; + +/** @type {import('@sveltejs/kit').Load} */ +export function load() { + throw redirect(301, 'https://example.com/redirected'); +} diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].svelte b/packages/kit/test/prerendering/basics/src/routes/redirect/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].svelte rename to packages/kit/test/prerendering/basics/src/routes/redirect/+page.svelte diff --git a/packages/kit/test/prerendering/basics/src/routes/shadowed-get.js b/packages/kit/test/prerendering/basics/src/routes/shadowed-get.js deleted file mode 100644 index 863b891d5c88..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/shadowed-get.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - answer: 42 - } - }; -} diff --git a/packages/kit/test/prerendering/basics/src/routes/shadowed-get.svelte b/packages/kit/test/prerendering/basics/src/routes/shadowed-get.svelte deleted file mode 100644 index f5f46beb9dcf..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/shadowed-get.svelte +++ /dev/null @@ -1,6 +0,0 @@ - - -

The answer is {answer}

diff --git a/packages/kit/test/prerendering/basics/src/routes/shadowed-get/+page.server.js b/packages/kit/test/prerendering/basics/src/routes/shadowed-get/+page.server.js new file mode 100644 index 000000000000..5c9429e18078 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/shadowed-get/+page.server.js @@ -0,0 +1,5 @@ +export function load() { + return { + answer: 42 + }; +} diff --git a/packages/kit/test/prerendering/basics/src/routes/shadowed-get/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/shadowed-get/+page.svelte new file mode 100644 index 000000000000..a475b1237f27 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/shadowed-get/+page.svelte @@ -0,0 +1,6 @@ + + +

The answer is {data.answer}

diff --git a/packages/kit/test/prerendering/basics/src/routes/shadowed-post.js b/packages/kit/test/prerendering/basics/src/routes/shadowed-post.js deleted file mode 100644 index ad7b3956ae0b..000000000000 --- a/packages/kit/test/prerendering/basics/src/routes/shadowed-post.js +++ /dev/null @@ -1,13 +0,0 @@ -export function GET() { - return { - body: { - answer: 42 - } - }; -} - -export function POST() { - return { - status: 201 - }; -} diff --git a/packages/kit/test/prerendering/basics/src/routes/shadowed-post/+page.server.js b/packages/kit/test/prerendering/basics/src/routes/shadowed-post/+page.server.js new file mode 100644 index 000000000000..4b2b54377b72 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/shadowed-post/+page.server.js @@ -0,0 +1,7 @@ +export function load() { + return { + answer: 42 + }; +} + +export function POST() {} diff --git a/packages/kit/test/prerendering/basics/src/routes/shadowed-post.svelte b/packages/kit/test/prerendering/basics/src/routes/shadowed-post/+page.svelte similarity index 100% rename from packages/kit/test/prerendering/basics/src/routes/shadowed-post.svelte rename to packages/kit/test/prerendering/basics/src/routes/shadowed-post/+page.svelte diff --git a/packages/kit/test/prerendering/basics/test/test.js b/packages/kit/test/prerendering/basics/test/test.js index 398d1916a175..8e0a66230cb2 100644 --- a/packages/kit/test/prerendering/basics/test/test.js +++ b/packages/kit/test/prerendering/basics/test/test.js @@ -52,7 +52,7 @@ test('renders page with data from endpoint', () => { test('renders page with unbuffered data from endpoint', () => { const content = read('fetch-endpoint/not-buffered.html'); - assert.ok(content.includes('

content-type: application/json; charset=utf-8

'), content); + assert.ok(content.includes('

content-type: application/json

'), content); const json = read('fetch-endpoint/not-buffered.json'); assert.equal(json, JSON.stringify({ answer: 42 })); @@ -64,11 +64,23 @@ test('loads a file with spaces in the filename', () => { }); test('generates __data.json file for shadow endpoints', () => { - assert.equal(read('__data.json'), JSON.stringify({ message: 'hello' })); - assert.equal(read('shadowed-get/__data.json'), JSON.stringify({ answer: 42 })); + assert.equal( + read('__data.json'), + JSON.stringify({ + type: 'data', + nodes: [{ data: null }, { data: { message: 'hello' } }] + }) + ); + assert.equal( + read('shadowed-get/__data.json'), + JSON.stringify({ + type: 'data', + nodes: [{ data: null }, { data: { answer: 42 } }] + }) + ); }); -test('does not prerender page with shadow endpoint with non-GET handler', () => { +test('does not prerender page with shadow endpoint with non-load handler', () => { assert.ok(!fs.existsSync(`${build}/shadowed-post.html`)); assert.ok(!fs.existsSync(`${build}/shadowed-post/__data.json`)); }); @@ -150,7 +162,13 @@ test('prerenders binary data', async () => { }); test('fetches data from local endpoint', () => { - assert.equal(read('origin/__data.json'), JSON.stringify({ message: 'hello' })); + assert.equal( + read('origin/__data.json'), + JSON.stringify({ + type: 'data', + nodes: [{ data: null }, { data: { message: 'hello' } }] + }) + ); assert.equal(read('origin/message.json'), JSON.stringify({ message: 'hello' })); }); diff --git a/packages/kit/test/prerendering/basics/vite.config.js b/packages/kit/test/prerendering/basics/vite.config.js index 7d997f333156..3c59318ad7c6 100644 --- a/packages/kit/test/prerendering/basics/vite.config.js +++ b/packages/kit/test/prerendering/basics/vite.config.js @@ -1,5 +1,5 @@ import * as path from 'path'; -import { plugin } from '../../utils.js'; +import { sveltekit } from '@sveltejs/kit/vite'; /** @type {import('vite').UserConfig} */ const config = { @@ -7,7 +7,7 @@ const config = { minify: false }, clearScreen: false, - plugins: [plugin()], + plugins: [sveltekit()], server: { fs: { allow: [path.resolve('../../../src')] diff --git a/packages/kit/test/prerendering/fallback/package.json b/packages/kit/test/prerendering/fallback/package.json index d9d2a6b05034..cd2f0c4c7c30 100644 --- a/packages/kit/test/prerendering/fallback/package.json +++ b/packages/kit/test/prerendering/fallback/package.json @@ -6,7 +6,7 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "check": "node ../../cli.js sync && tsc && svelte-check", + "check": "svelte-kit sync && tsc && svelte-check", "test": "npm run build" }, "devDependencies": { diff --git a/packages/kit/test/prerendering/fallback/src/routes/index.svelte b/packages/kit/test/prerendering/fallback/src/routes/+page.svelte similarity index 100% rename from packages/kit/test/prerendering/fallback/src/routes/index.svelte rename to packages/kit/test/prerendering/fallback/src/routes/+page.svelte diff --git a/packages/kit/test/prerendering/fallback/vite.config.js b/packages/kit/test/prerendering/fallback/vite.config.js index 7d997f333156..3c59318ad7c6 100644 --- a/packages/kit/test/prerendering/fallback/vite.config.js +++ b/packages/kit/test/prerendering/fallback/vite.config.js @@ -1,5 +1,5 @@ import * as path from 'path'; -import { plugin } from '../../utils.js'; +import { sveltekit } from '@sveltejs/kit/vite'; /** @type {import('vite').UserConfig} */ const config = { @@ -7,7 +7,7 @@ const config = { minify: false }, clearScreen: false, - plugins: [plugin()], + plugins: [sveltekit()], server: { fs: { allow: [path.resolve('../../../src')] diff --git a/packages/kit/test/prerendering/options/package.json b/packages/kit/test/prerendering/options/package.json index 29b138f5b496..f83b597edf9a 100644 --- a/packages/kit/test/prerendering/options/package.json +++ b/packages/kit/test/prerendering/options/package.json @@ -6,7 +6,7 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "check": "node ../../cli.js sync && tsc && svelte-check", + "check": "svelte-kit sync && tsc && svelte-check", "test": "npm run build && uvu test" }, "devDependencies": { diff --git a/packages/kit/test/prerendering/options/src/routes/index.svelte b/packages/kit/test/prerendering/options/src/routes/+page.svelte similarity index 100% rename from packages/kit/test/prerendering/options/src/routes/index.svelte rename to packages/kit/test/prerendering/options/src/routes/+page.svelte diff --git a/packages/kit/test/prerendering/options/src/routes/nested/index.svelte b/packages/kit/test/prerendering/options/src/routes/nested/+page.svelte similarity index 100% rename from packages/kit/test/prerendering/options/src/routes/nested/index.svelte rename to packages/kit/test/prerendering/options/src/routes/nested/+page.svelte diff --git a/packages/kit/test/prerendering/options/vite.config.js b/packages/kit/test/prerendering/options/vite.config.js index 7d997f333156..3c59318ad7c6 100644 --- a/packages/kit/test/prerendering/options/vite.config.js +++ b/packages/kit/test/prerendering/options/vite.config.js @@ -1,5 +1,5 @@ import * as path from 'path'; -import { plugin } from '../../utils.js'; +import { sveltekit } from '@sveltejs/kit/vite'; /** @type {import('vite').UserConfig} */ const config = { @@ -7,7 +7,7 @@ const config = { minify: false }, clearScreen: false, - plugins: [plugin()], + plugins: [sveltekit()], server: { fs: { allow: [path.resolve('../../../src')] diff --git a/packages/kit/test/prerendering/paths-base/package.json b/packages/kit/test/prerendering/paths-base/package.json index 13950563cfdc..6ad24e692f7a 100644 --- a/packages/kit/test/prerendering/paths-base/package.json +++ b/packages/kit/test/prerendering/paths-base/package.json @@ -6,7 +6,7 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "check": "node ../../cli.js sync && tsc && svelte-check", + "check": "svelte-kit sync && tsc && svelte-check", "test": "npm run build && uvu test" }, "devDependencies": { diff --git a/packages/kit/test/prerendering/paths-base/src/routes/+page.js b/packages/kit/test/prerendering/paths-base/src/routes/+page.js new file mode 100644 index 000000000000..84d4247b57a2 --- /dev/null +++ b/packages/kit/test/prerendering/paths-base/src/routes/+page.js @@ -0,0 +1,6 @@ +import { redirect } from '@sveltejs/kit'; +import { base } from '$app/paths'; + +export function load() { + throw redirect(301, `${base}/dynamic/foo`); +} diff --git a/packages/kit/test/prerendering/paths-base/src/routes/dynamic/[slug].svelte b/packages/kit/test/prerendering/paths-base/src/routes/dynamic/[slug]/+page.svelte similarity index 100% rename from packages/kit/test/prerendering/paths-base/src/routes/dynamic/[slug].svelte rename to packages/kit/test/prerendering/paths-base/src/routes/dynamic/[slug]/+page.svelte diff --git a/packages/kit/test/prerendering/paths-base/src/routes/index.svelte b/packages/kit/test/prerendering/paths-base/src/routes/index.svelte deleted file mode 100644 index ba61d6448419..000000000000 --- a/packages/kit/test/prerendering/paths-base/src/routes/index.svelte +++ /dev/null @@ -1,10 +0,0 @@ - \ No newline at end of file diff --git a/packages/kit/test/prerendering/paths-base/src/routes/nested/index.svelte b/packages/kit/test/prerendering/paths-base/src/routes/nested/+page.svelte similarity index 100% rename from packages/kit/test/prerendering/paths-base/src/routes/nested/index.svelte rename to packages/kit/test/prerendering/paths-base/src/routes/nested/+page.svelte diff --git a/packages/kit/test/prerendering/paths-base/vite.config.js b/packages/kit/test/prerendering/paths-base/vite.config.js index 7d997f333156..3c59318ad7c6 100644 --- a/packages/kit/test/prerendering/paths-base/vite.config.js +++ b/packages/kit/test/prerendering/paths-base/vite.config.js @@ -1,5 +1,5 @@ import * as path from 'path'; -import { plugin } from '../../utils.js'; +import { sveltekit } from '@sveltejs/kit/vite'; /** @type {import('vite').UserConfig} */ const config = { @@ -7,7 +7,7 @@ const config = { minify: false }, clearScreen: false, - plugins: [plugin()], + plugins: [sveltekit()], server: { fs: { allow: [path.resolve('../../../src')] diff --git a/packages/kit/test/prerendering/trailing-slash/package.json b/packages/kit/test/prerendering/trailing-slash/package.json index 8038af7c7045..2fa3e9d82529 100644 --- a/packages/kit/test/prerendering/trailing-slash/package.json +++ b/packages/kit/test/prerendering/trailing-slash/package.json @@ -6,7 +6,7 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "check": "node ../../cli.js sync && tsc && svelte-check", + "check": "svelte-kit sync && tsc && svelte-check", "test": "npm run build && uvu test" }, "devDependencies": { diff --git a/packages/kit/test/prerendering/trailing-slash/src/routes/index.svelte b/packages/kit/test/prerendering/trailing-slash/src/routes/+page.svelte similarity index 100% rename from packages/kit/test/prerendering/trailing-slash/src/routes/index.svelte rename to packages/kit/test/prerendering/trailing-slash/src/routes/+page.svelte diff --git a/packages/kit/test/prerendering/trailing-slash/src/routes/page.js b/packages/kit/test/prerendering/trailing-slash/src/routes/page.js deleted file mode 100644 index 6fe9d378bc26..000000000000 --- a/packages/kit/test/prerendering/trailing-slash/src/routes/page.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - message: 'hello' - } - }; -} diff --git a/packages/kit/test/prerendering/trailing-slash/src/routes/page.svelte b/packages/kit/test/prerendering/trailing-slash/src/routes/page.svelte deleted file mode 100644 index 7c991db4005a..000000000000 --- a/packages/kit/test/prerendering/trailing-slash/src/routes/page.svelte +++ /dev/null @@ -1,6 +0,0 @@ - - -

{message}

diff --git a/packages/kit/test/prerendering/trailing-slash/src/routes/page/+page.server.js b/packages/kit/test/prerendering/trailing-slash/src/routes/page/+page.server.js new file mode 100644 index 000000000000..f10d94d2ba04 --- /dev/null +++ b/packages/kit/test/prerendering/trailing-slash/src/routes/page/+page.server.js @@ -0,0 +1,5 @@ +export function load() { + return { + message: 'hello' + }; +} diff --git a/packages/kit/test/prerendering/trailing-slash/src/routes/page/+page.svelte b/packages/kit/test/prerendering/trailing-slash/src/routes/page/+page.svelte new file mode 100644 index 000000000000..65b84bb82fa9 --- /dev/null +++ b/packages/kit/test/prerendering/trailing-slash/src/routes/page/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.message}

diff --git a/packages/kit/test/prerendering/trailing-slash/src/routes/standalone-endpoint.json.js b/packages/kit/test/prerendering/trailing-slash/src/routes/standalone-endpoint.json.js deleted file mode 100644 index 863b891d5c88..000000000000 --- a/packages/kit/test/prerendering/trailing-slash/src/routes/standalone-endpoint.json.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return { - body: { - answer: 42 - } - }; -} diff --git a/packages/kit/test/prerendering/trailing-slash/src/routes/standalone-endpoint.json/+server.js b/packages/kit/test/prerendering/trailing-slash/src/routes/standalone-endpoint.json/+server.js new file mode 100644 index 000000000000..82d3cb7b8345 --- /dev/null +++ b/packages/kit/test/prerendering/trailing-slash/src/routes/standalone-endpoint.json/+server.js @@ -0,0 +1,5 @@ +import { json } from '@sveltejs/kit'; + +export async function GET() { + return json({ answer: 42 }); +} diff --git a/packages/kit/test/prerendering/trailing-slash/vite.config.js b/packages/kit/test/prerendering/trailing-slash/vite.config.js index 7d997f333156..3c59318ad7c6 100644 --- a/packages/kit/test/prerendering/trailing-slash/vite.config.js +++ b/packages/kit/test/prerendering/trailing-slash/vite.config.js @@ -1,5 +1,5 @@ import * as path from 'path'; -import { plugin } from '../../utils.js'; +import { sveltekit } from '@sveltejs/kit/vite'; /** @type {import('vite').UserConfig} */ const config = { @@ -7,7 +7,7 @@ const config = { minify: false }, clearScreen: false, - plugins: [plugin()], + plugins: [sveltekit()], server: { fs: { allow: [path.resolve('../../../src')] diff --git a/packages/kit/test/typings/endpoint.test.ts b/packages/kit/test/typings/endpoint.test.ts deleted file mode 100644 index 558bd6d80719..000000000000 --- a/packages/kit/test/typings/endpoint.test.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { RequestHandler } from '@sveltejs/kit'; - -const valid_body = { - str: 'string', - num: 12345, - bool: true, - null: null, - maybe: Math.random() < 0.5 ? undefined : true, - custom: { - toJSON: () => 'custom toJSON function' - }, - list: ['string', 12345, false, null, undefined], - nested: { - another: 'string', - big_num: 98765, - binary: false, - nullish: null, - custom: { - toJSON: () => 'hi mom' - }, - list: [], - deeply: { - nested: {} - } - } -}; - -// valid - basic case of returning an object -export const base_case: RequestHandler = () => { - return { - body: valid_body - }; -}; - -// valid - raw Response instance should be allowed -export const response_instance: RequestHandler = () => { - return new Response(); -}; - -// valid - body instance of Uint8Array should be allowed -export const uint8array_body: RequestHandler = () => { - return { - body: new Uint8Array() - }; -}; - -// valid - body instance of ReadableStream should be allowed -export const readable_stream_body: RequestHandler = () => { - return { - body: new ReadableStream() - }; -}; - -// valid - different header pairs should be allowed -export const differential_headers_assignment: RequestHandler = () => { - if (Math.random() < 0.5) { - return { headers: { foo: 'bar' } }; - } else { - return { headers: { baz: 'foo' } }; - } -}; - -/** - * NOTE about type casting in body returned - * - * tests below with `{} as Interface` casts are there only for - * convenience purposes so we won't have to actually fill in the - * required data, it serves the exact same purpose and doesn't - * make the tests invalid - */ - -/** example json-serializable POJO */ -interface ExamplePost { - title: string; - description: string; - published_date?: string; - author_name?: string; - author_link?: string; -} -// valid - should not be any different -export const generic_case: RequestHandler, ExamplePost> = () => { - return { - body: {} as ExamplePost - }; -}; - -interface NestedChild { - message: string; -} -interface ParentWrapper { - fields: NestedChild; -} -export const nested_interfaces: RequestHandler, ParentWrapper> = () => { - return { - body: {} as ParentWrapper - }; -}; - -interface NestedLiteral { - date: { - published: string; - updated?: string; - time: { - hour: number; - min: number; - sec?: number; - }; - }; -} -export const nested_literal: RequestHandler, NestedLiteral> = () => { - return { - body: {} as NestedLiteral - }; -}; - -// --- invalid cases --- - -// @ts-expect-error - bigint cannot be converted to JSON string -export const error_unserializable_literal_bigint: RequestHandler = () => { - return { - body: BigInt('') - }; -}; -// @ts-expect-error - bigint cannot be converted to JSON string -export const error_unserializable_property_bigint: RequestHandler = () => { - return { - body: { - answer: BigInt('') - } - }; -}; -// @ts-expect-error - function cannot be converted to JSON string -export const error_unserializable_literal_function: RequestHandler = () => { - return { - body: () => {} - }; -}; -// @ts-expect-error - function cannot be converted to JSON string -export const error_unserializable_property_function: RequestHandler = () => { - return { - body: { - sorter() {} - } - }; -}; -// @ts-expect-error - symbol cannot be converted to JSON string -export const error_unserializable_literal_symbol: RequestHandler = () => { - return { - body: Symbol() - }; -}; -// @ts-expect-error - symbol cannot be converted to JSON string -export const error_unserializable_property_symbol: RequestHandler = () => { - return { - body: { - id: Symbol() - } - }; -}; - -/** example object that isn't serializable */ -interface InvalidPost { - sorter(a: any, b: any): number; -} -// @ts-expect-error - body must be JSON serializable with Generic passed -export const error_unserializable_generic: RequestHandler< - Record, - InvalidPost -> = () => { - return { - body: {} as InvalidPost - }; -}; - -// @ts-expect-error - body typed array must only be Uint8Array -export const error_other_typed_array_instances: RequestHandler = () => { - return { - body: new Uint16Array() - }; -}; - -// @ts-expect-error - instances cannot be nested -export const error_nested_instances: RequestHandler = () => { - return { - body: { typed: new Uint8Array() } - }; -}; - -// @ts-expect-error - instances cannot be nested -export const stream_readable_body: RequestHandler = async () => { - const { Readable } = await import('stream'); - return { - body: new Readable() - }; -}; diff --git a/packages/kit/test/typings/tsconfig.json b/packages/kit/test/typings/tsconfig.json deleted file mode 100644 index 9beabd92b571..000000000000 --- a/packages/kit/test/typings/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "noEmit": true - }, - "include": ["**/*.test.ts"] -} diff --git a/packages/kit/test/utils.js b/packages/kit/test/utils.js index 017f47585ae3..07d367f93d1e 100644 --- a/packages/kit/test/utils.js +++ b/packages/kit/test/utils.js @@ -205,7 +205,3 @@ export async function start_server(handler) { } }; } - -export const plugin = process.env.CI - ? (await import('../dist/vite.js')).sveltekit - : (await import('../src/vite/index.js')).sveltekit; diff --git a/packages/kit/types/ambient.d.ts b/packages/kit/types/ambient.d.ts index 00c18066a3ed..a19376eeae5a 100644 --- a/packages/kit/types/ambient.d.ts +++ b/packages/kit/types/ambient.d.ts @@ -14,8 +14,6 @@ * interface PublicEnv {} * * interface Session {} - * - * interface Stuff {} * } * ``` * @@ -57,24 +55,19 @@ declare namespace App { export interface Platform {} /** - * The interface that defines the dynamic environment variables exported from '$env/dynamic/private'. + * The interface that defines the dynamic environment variables exported from `$env/dynamic/private`. */ export interface PrivateEnv extends Record {} /** - * The interface that defines the dynamic environment variables exported from '$env/dynamic/public'. + * The interface that defines the dynamic environment variables exported from `$env/dynamic/public`. */ export interface PublicEnv extends Record {} /** - * The interface that defines `session`, both as an argument to [`load`](https://kit.svelte.dev/docs/loading) functions and the value of the [session store](https://kit.svelte.dev/docs/modules#$app-stores). + * The interface that defines `session`, both as an argument to [`load`](https://kit.svelte.dev/docs/load) functions and the value of the [session store](https://kit.svelte.dev/docs/modules#$app-stores). */ export interface Session {} - - /** - * The interface that defines `stuff`, as input or output to [`load`](https://kit.svelte.dev/docs/loading) or as the value of the `stuff` property of the [page store](https://kit.svelte.dev/docs/modules#$app-stores). - */ - export interface Stuff {} } /** @@ -165,10 +158,10 @@ declare module '$app/navigation' { opts?: { replaceState?: boolean; noscroll?: boolean; keepfocus?: boolean; state?: any } ): Promise; /** - * Causes any `load` functions belonging to the currently active page to re-run if they `fetch` the resource in question, or re-fetches data from a page endpoint if the invalidated resource is the page itself. Returns a `Promise` that resolves when the page is subsequently updated. + * Causes any `load` functions belonging to the currently active page to re-run if they `fetch` the resource in question, or re-fetches data from a page endpoint if the invalidated resource is the page itself. If no argument is given, all resources will be invalidated. Returns a `Promise` that resolves when the page is subsequently updated. * @param dependency The invalidated resource */ - export function invalidate(dependency: string | ((href: string) => boolean)): Promise; + export function invalidate(dependency?: string | ((href: string) => boolean)): Promise; /** * Programmatically prefetches the given page, which means * 1. ensuring that the code for the page is loaded, and diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 591d6104f0d4..fa56a17111d6 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -6,8 +6,8 @@ import './ambient.js'; import { CompileOptions } from 'svelte/types/compiler/interfaces'; import { AdapterEntry, - BodyValidator, CspDirectives, + JSONObject, JSONValue, Logger, MaybePromise, @@ -19,12 +19,29 @@ import { TrailingSlash } from './private.js'; import { SSRNodeLoader, SSRRoute, ValidatedConfig } from './internal.js'; +import { HttpError, Redirect } from '../src/index/private.js'; export interface Adapter { name: string; adapt(builder: Builder): MaybePromise; } +export type AwaitedProperties | void> = input extends void + ? undefined // needs to be undefined, because void will break intellisense + : input extends Record + ? { + [key in keyof input]: Awaited; + } + : {} extends input // handles the any case + ? input + : unknown; + +export type AwaitedErrors any> = Awaited> extends { + errors?: any; +} + ? Awaited>['errors'] + : undefined; + export interface Builder { log: Logger; rimraf(dir: string): void; @@ -145,7 +162,6 @@ export interface KitConfig { onError?: PrerenderOnErrorValue; origin?: string; }; - routes?: (filepath: string) => boolean; serviceWorker?: { register?: boolean; files?: (filepath: string) => boolean; @@ -177,46 +193,32 @@ export interface HandleError { } /** - * The `(event: LoadEvent) => LoadOutput` `load` function exported from ` - - {status} + {$page.status}
{#if online}

Yikes!

- {#if error.message} -

{status}: {error.message}

+ {#if $page.error.message} +

{$page.status}: {$page.error.message}

{:else} -

Encountered a {status} error

+

Encountered a {$page.status} error

{/if} - {#if status >= 500} - {#if dev && error.stack} -
{error.stack}
+ {#if $page.status >= 500} + {#if dev && $page.error.stack} +
{$page.error.stack}
{:else}

Please try reloading the page.

- If the error persists, please drop by Discord chatroom + If the error persists, please drop by Discord chatroom and let us know, or raise an issue on GitHub. Thanks!

@@ -79,8 +71,4 @@ font: 600 16px/1.7 var(--font); border-radius: 2px; } - - /* @media (min-width: 480px) { - h1 { font-size: 4em } - } */ diff --git a/sites/kit.svelte.dev/src/routes/__layout.svelte b/sites/kit.svelte.dev/src/routes/+layout.svelte similarity index 100% rename from sites/kit.svelte.dev/src/routes/__layout.svelte rename to sites/kit.svelte.dev/src/routes/+layout.svelte diff --git a/sites/kit.svelte.dev/src/routes/+page.js b/sites/kit.svelte.dev/src/routes/+page.js new file mode 100644 index 000000000000..ad7a278713ef --- /dev/null +++ b/sites/kit.svelte.dev/src/routes/+page.js @@ -0,0 +1,5 @@ +export function load({ setHeaders }) { + setHeaders({ + 'cache-control': 'public, max-age=60' + }); +} diff --git a/sites/kit.svelte.dev/src/routes/index.svelte b/sites/kit.svelte.dev/src/routes/+page.svelte similarity index 92% rename from sites/kit.svelte.dev/src/routes/index.svelte rename to sites/kit.svelte.dev/src/routes/+page.svelte index 82257ffd6c66..0207d176115a 100644 --- a/sites/kit.svelte.dev/src/routes/index.svelte +++ b/sites/kit.svelte.dev/src/routes/+page.svelte @@ -1,11 +1,3 @@ - - - - - {section.title} • Docs • SvelteKit + {data.section.title} • Docs • SvelteKit - - + +
-

{section.title}

+

{data.section.title}

- + Edit this page on GitHub
- {@html section.content} + {@html data.section.content}
- previous - {#if prev} - {prev.title} + previous + {#if data.prev} + {data.prev.title} {/if}
- next - {#if next} - {next.title} + next + {#if data.next} + {data.next.title} {/if}
diff --git a/sites/kit.svelte.dev/src/routes/docs/index.json.js b/sites/kit.svelte.dev/src/routes/docs/index.json.js deleted file mode 100644 index a1bb571fffaa..000000000000 --- a/sites/kit.svelte.dev/src/routes/docs/index.json.js +++ /dev/null @@ -1,7 +0,0 @@ -import { read_headings } from '$lib/docs/server'; - -export function GET() { - return { - body: read_headings('docs') - }; -} diff --git a/sites/kit.svelte.dev/src/routes/docs/index.svelte b/sites/kit.svelte.dev/src/routes/docs/index.svelte deleted file mode 100644 index f70429adf707..000000000000 --- a/sites/kit.svelte.dev/src/routes/docs/index.svelte +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/sites/kit.svelte.dev/src/routes/faq/+page.server.js b/sites/kit.svelte.dev/src/routes/faq/+page.server.js new file mode 100644 index 000000000000..3f302cce0c41 --- /dev/null +++ b/sites/kit.svelte.dev/src/routes/faq/+page.server.js @@ -0,0 +1,7 @@ +import { read_all } from '$lib/docs/server'; + +export async function load() { + return { + sections: await read_all('faq') + }; +} diff --git a/sites/kit.svelte.dev/src/routes/faq/index.svelte b/sites/kit.svelte.dev/src/routes/faq/+page.svelte similarity index 97% rename from sites/kit.svelte.dev/src/routes/faq/index.svelte rename to sites/kit.svelte.dev/src/routes/faq/+page.svelte index 0cb5b5f02f33..05c28ac433b2 100644 --- a/sites/kit.svelte.dev/src/routes/faq/index.svelte +++ b/sites/kit.svelte.dev/src/routes/faq/+page.svelte @@ -5,7 +5,8 @@ import '@sveltejs/site-kit/code.css'; import * as hovers from '$lib/docs/client/hovers.js'; - export let sections; + /** @type {import('./$types').Data} */ + export let data; hovers.setup(); @@ -20,7 +21,7 @@

Frequently Asked Questions

- {#each sections as faq} + {#each data.sections as faq}

{faq.title} diff --git a/sites/kit.svelte.dev/src/routes/faq/index.js b/sites/kit.svelte.dev/src/routes/faq/index.js deleted file mode 100644 index 65c4a6a63898..000000000000 --- a/sites/kit.svelte.dev/src/routes/faq/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import { read_all } from '$lib/docs/server'; - -export async function GET() { - return { - body: { - sections: await read_all('faq') - } - }; -} diff --git a/sites/kit.svelte.dev/src/routes/search/+page.js b/sites/kit.svelte.dev/src/routes/search/+page.js new file mode 100644 index 000000000000..d43d0cd2a55d --- /dev/null +++ b/sites/kit.svelte.dev/src/routes/search/+page.js @@ -0,0 +1 @@ +export const prerender = false; diff --git a/sites/kit.svelte.dev/src/routes/search/+page.server.js b/sites/kit.svelte.dev/src/routes/search/+page.server.js new file mode 100644 index 000000000000..f229f5d72ef4 --- /dev/null +++ b/sites/kit.svelte.dev/src/routes/search/+page.server.js @@ -0,0 +1,18 @@ +import { content } from '$lib/search/content.server.js'; +import { init, inited, search } from '$lib/search/search.js'; + +/** @type {import('./$types').PageServerLoad} */ +export async function load({ url }) { + if (!inited) { + init(content()); + } + + const query = url.searchParams.get('q'); + + const results = search(query); + + return { + query, + results + }; +} diff --git a/sites/kit.svelte.dev/src/routes/search/index.svelte b/sites/kit.svelte.dev/src/routes/search/+page.svelte similarity index 69% rename from sites/kit.svelte.dev/src/routes/search/index.svelte rename to sites/kit.svelte.dev/src/routes/search/+page.svelte index 92505a780fd0..b3b640f8b18e 100644 --- a/sites/kit.svelte.dev/src/routes/search/index.svelte +++ b/sites/kit.svelte.dev/src/routes/search/+page.svelte @@ -1,15 +1,8 @@ - - @@ -18,10 +11,10 @@
- + - +