diff --git a/.changeset/angry-pugs-play.md b/.changeset/angry-pugs-play.md new file mode 100644 index 000000000000..323590c972ff --- /dev/null +++ b/.changeset/angry-pugs-play.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Prevent double-fixing of error stack traces in dev mode diff --git a/.changeset/breezy-onions-remember.md b/.changeset/breezy-onions-remember.md new file mode 100644 index 000000000000..0f1e44f4c91a --- /dev/null +++ b/.changeset/breezy-onions-remember.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +create `__data.json` for pathnames with trailing slashes, including `/` diff --git a/.changeset/clean-islands-do.md b/.changeset/clean-islands-do.md new file mode 100644 index 000000000000..79eb726c7db5 --- /dev/null +++ b/.changeset/clean-islands-do.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[fix] remove private methods to make Safari 14.1 work diff --git a/.changeset/curvy-rivers-share.md b/.changeset/curvy-rivers-share.md new file mode 100644 index 000000000000..d69be1dd53b9 --- /dev/null +++ b/.changeset/curvy-rivers-share.md @@ -0,0 +1,5 @@ +--- +"create-svelte": patch +--- + +[fix] update docs URL for App namespace interfaces diff --git a/.changeset/cyan-cobras-explode.md b/.changeset/cyan-cobras-explode.md new file mode 100644 index 000000000000..fdb170084db8 --- /dev/null +++ b/.changeset/cyan-cobras-explode.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[breaking] referer header sent by fetch in load matches page's referer header, not the page itself diff --git a/.changeset/eight-chicken-sin.md b/.changeset/eight-chicken-sin.md new file mode 100644 index 000000000000..7695a508e948 --- /dev/null +++ b/.changeset/eight-chicken-sin.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Disable meta http-equiv tags for static amp configuration diff --git a/.changeset/four-ties-destroy.md b/.changeset/four-ties-destroy.md new file mode 100644 index 000000000000..e8eab00e6f5e --- /dev/null +++ b/.changeset/four-ties-destroy.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-auto': patch +--- + +Attempt to bump @next version of adapter-auto diff --git a/.changeset/great-seahorses-fry.md b/.changeset/great-seahorses-fry.md new file mode 100644 index 000000000000..fd5436210718 --- /dev/null +++ b/.changeset/great-seahorses-fry.md @@ -0,0 +1,5 @@ +--- +'create-svelte': patch +--- + +fix `@typescript-eslint/no-empty-interface` lint error when starting a new app with eslint diff --git a/.changeset/green-garlics-warn.md b/.changeset/green-garlics-warn.md new file mode 100644 index 000000000000..6ee4326c186a --- /dev/null +++ b/.changeset/green-garlics-warn.md @@ -0,0 +1,5 @@ +--- +'create-svelte': patch +--- + +Add option to create integration tests with Playwright diff --git a/.changeset/hip-windows-sit.md b/.changeset/hip-windows-sit.md new file mode 100644 index 000000000000..175080dd06e6 --- /dev/null +++ b/.changeset/hip-windows-sit.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/adapter-auto': patch +'@sveltejs/adapter-cloudflare': patch +--- + +Bumping versions again diff --git a/.changeset/little-geckos-smell.md b/.changeset/little-geckos-smell.md new file mode 100644 index 000000000000..f7e2f421c7a0 --- /dev/null +++ b/.changeset/little-geckos-smell.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[breaking] remove sveltekit:navigation-{start,end} events diff --git a/.changeset/old-years-march.md b/.changeset/old-years-march.md new file mode 100644 index 000000000000..c51205ec9d8b --- /dev/null +++ b/.changeset/old-years-march.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[breaking] defer pushState until navigation occurs diff --git a/.changeset/pre.json b/.changeset/pre.json index 788b98ba8e1b..d16e61f83bd5 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -10,10 +10,10 @@ "@sveltejs/app-utils": "0.0.18", "create-svelte": "2.0.0-alpha.19", "@sveltejs/kit": "0.0.30", - "@sveltejs/vite-plugin-svelte": "1.0.0-next.0", + "@sveltejs/vite-plugin-svelte": "0.0.1", "@sveltejs/adapter-cloudflare-workers": "0.0.1", - "@sveltejs/adapter-cloudflare": "1.0.0-next.0", - "@sveltejs/adapter-auto": "1.0.0-next", + "@sveltejs/adapter-cloudflare": "0.0.1", + "@sveltejs/adapter-auto": "0.0.1", "set-env-action": "1.0.0", "~TODO~": "0.0.1", "prerendering-test-basics": "0.0.1", @@ -51,6 +51,7 @@ "brave-turkeys-bathe", "brave-weeks-allow", "breezy-games-count", + "breezy-onions-remember", "breezy-sheep-dress", "breezy-students-refuse", "bright-cherries-hug", @@ -88,6 +89,7 @@ "clean-camels-pump", "clean-chicken-walk", "clean-countries-push", + "clean-islands-do", "clever-dolls-poke", "clever-donuts-smile", "clever-eagles-live", @@ -115,9 +117,11 @@ "curvy-dots-join", "curvy-eggs-sip", "curvy-experts-try", + "curvy-rivers-share", "curvy-sloths-brake", "curvy-zebras-tan", "cyan-ads-pump", + "cyan-cobras-explode", "cyan-knives-mix", "cyan-numbers-change", "cyan-parents-enjoy", @@ -139,6 +143,7 @@ "early-snakes-peel", "early-wasps-obey", "eight-birds-run", + "eight-chicken-sin", "eight-glasses-sparkle", "eight-hats-worry", "eight-keys-give", @@ -195,6 +200,7 @@ "four-cheetahs-yawn", "four-news-turn", "four-pillows-give", + "four-ties-destroy", "fresh-adults-ring", "fresh-dolls-destroy", "fresh-hats-tie", @@ -238,6 +244,8 @@ "great-guests-visit", "great-kangaroos-eat", "great-queens-rule", + "great-seahorses-fry", + "green-garlics-warn", "green-mayflies-shave", "green-spoons-count", "grumpy-days-compete", @@ -265,6 +273,7 @@ "hip-nails-burn", "hip-nails-taste", "hip-walls-flash", + "hip-windows-sit", "honest-beers-sing", "honest-bikes-pull", "honest-jars-report", @@ -323,6 +332,7 @@ "light-roses-teach", "little-boats-allow", "little-candles-trade", + "little-geckos-smell", "little-shirts-happen", "little-tables-decide", "long-bulldogs-invent", @@ -414,6 +424,7 @@ "odd-ligers-swim", "old-ears-wait", "old-files-jump", + "old-years-march", "olive-frogs-add", "olive-frogs-drive", "olive-maps-join", @@ -685,6 +696,7 @@ "tidy-pigs-peel", "tidy-turkeys-rule", "tidy-wasps-shave", + "tiny-badgers-love", "tiny-candles-repeat", "tiny-files-smile", "tiny-gorillas-whisper", @@ -712,6 +724,7 @@ "twelve-feet-deny", "twelve-goats-knock", "twelve-onions-burn", + "twenty-dogs-shout", "twenty-dryers-hope", "twenty-hairs-shave", "twenty-numbers-destroy", diff --git a/.changeset/serious-panthers-sing.md b/.changeset/serious-panthers-sing.md new file mode 100644 index 000000000000..98e601f414a3 --- /dev/null +++ b/.changeset/serious-panthers-sing.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Expose Navigation type diff --git a/.changeset/stupid-beds-prove.md b/.changeset/stupid-beds-prove.md new file mode 100644 index 000000000000..650e8f98a09c --- /dev/null +++ b/.changeset/stupid-beds-prove.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +add new `Page` type diff --git a/.changeset/three-elephants-breathe.md b/.changeset/three-elephants-breathe.md new file mode 100644 index 000000000000..4da4430afc1f --- /dev/null +++ b/.changeset/three-elephants-breathe.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-netlify': patch +--- + +Fix incorrect set-cookie header handling on adapter-netlify diff --git a/.changeset/tiny-badgers-love.md b/.changeset/tiny-badgers-love.md new file mode 100644 index 000000000000..4d38e8402957 --- /dev/null +++ b/.changeset/tiny-badgers-love.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Ignore click event if url does not have origin (e.g. `mailto:`, `tel:`) diff --git a/.changeset/twenty-dogs-shout.md b/.changeset/twenty-dogs-shout.md new file mode 100644 index 000000000000..2a5d994c15b0 --- /dev/null +++ b/.changeset/twenty-dogs-shout.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/adapter-auto': patch +'@sveltejs/adapter-cloudflare': patch +--- + +Attempt to force @next version bump diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91bb939b2c9e..2ec4201f039d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: pnpm/action-setup@v2.1.0 + - uses: pnpm/action-setup@v2.2.0 with: version: 6.23.2 - uses: actions/setup-node@v2 @@ -39,7 +39,7 @@ jobs: steps: - run: git config --global core.autocrlf false - uses: actions/checkout@v2 - - uses: pnpm/action-setup@v2.1.0 + - uses: pnpm/action-setup@v2.2.0 with: version: 6.23.2 - uses: actions/setup-node@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 41bdc990d78c..24da638eb7c1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: with: # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits fetch-depth: 0 - - uses: pnpm/action-setup@v2.1.0 + - uses: pnpm/action-setup@v2.2.0 with: version: 6.23.2 - name: Setup Node.js diff --git a/documentation/docs/01-routing.md b/documentation/docs/01-routing.md index 3a31571733dd..425ffbf90b51 100644 --- a/documentation/docs/01-routing.md +++ b/documentation/docs/01-routing.md @@ -45,9 +45,7 @@ A file or directory can have multiple dynamic parts, like `[id]-[category].svelt ### Endpoints -Endpoints are modules written in `.js` (or `.ts`) files that export functions corresponding to HTTP methods. Their job is to allow pages to read and write data that is only available on the server (for example in a database, or on the filesystem). - -If an endpoint has the same filename as a page (except for the extension), the page will get its props from the endpoint. So a page like `src/routes/items/[id].svelte` could get its props from this file: +Endpoints are modules written in `.js` (or `.ts`) files that export [request handler](/docs/types#sveltejs-kit-requesthandler) functions corresponding to HTTP methods. Their job is to make it possible to read and write data that is only available on the server (for example in a database, or on the filesystem). ```js /// file: src/routes/items/[id].js @@ -81,7 +79,7 @@ export async function get({ params }) { > All server-side code, including endpoints, has access to `fetch` in case you need to request data from external APIs. Don't worry about the `$lib` import, we'll get to that [later](/docs/modules#$lib). -The job of this function is to return a `{ status, headers, body }` object representing the response, where `status` is an [HTTP status code](https://httpstatusdogs.com): +The job of a [request handler](/docs/types#sveltejs-kit-requesthandler) is to return a `{ status, headers, body }` object representing the response, where `status` is an [HTTP status code](https://httpstatusdogs.com): - `2xx` — successful response (default is `200`) - `3xx` — redirection (should be accompanied by a `location` header) @@ -90,7 +88,11 @@ The job of this function is to return a `{ status, headers, body }` object repre > If `{fallthrough: true}` is returned SvelteKit will [fall through](/docs/routing#advanced-routing-fallthrough-routes) to other routes until something responds, or will respond with a generic 404. -The returned `body` corresponds to the page's props: +#### Page endpoints + +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. + +A page like `src/routes/items/[id].svelte` could get its props from the `body` in the endpoint above: ```svelte /// file: src/routes/items/[id].svelte @@ -102,6 +104,23 @@ The returned `body` corresponds to the page's props:

{item.title}

``` +Because the page and route have the same URL, you will need to include an `accept: application/json` header to get JSON from the endpoint rather than HTML from the page. You can also get the raw data by appending `/__data.json` to the URL, e.g. `/items/__data.json`. + +#### Standalone endpoints + +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, they can return a `Uint8Array`. + +Standalone endpoints can be given a file extension if desired, or accessed directly if not: + +| 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 | + +> Support for streaming request and response bodies is [coming soon](https://github.com/sveltejs/kit/issues/3419). + #### POST, PUT, PATCH, DELETE Endpoints can handle any HTTP method — not just `GET` — by exporting the corresponding function: @@ -191,8 +210,6 @@ export async function post({ request }) { ``` -If you request the route with an `accept: application/json` header, SvelteKit will render the endpoint data as JSON, rather than the page as HTML. - #### Body parsing 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: @@ -263,12 +280,6 @@ export default config; > Using native `
` behaviour ensures your app continues to work when JavaScript fails or is disabled. -### Standalone endpoints - -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, they can return a string or a `Uint8Array`. - -> Support for streaming request and response bodies is [coming soon](https://github.com/sveltejs/kit/issues/3419). - ### Private modules 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. diff --git a/documentation/docs/08-events.md b/documentation/docs/08-events.md index 58d01a516ba6..205b399b30ca 100644 --- a/documentation/docs/08-events.md +++ b/documentation/docs/08-events.md @@ -2,10 +2,6 @@ title: Events --- -SvelteKit emits [CustomEvents](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) on the `window` object when certain things happen: +SvelteKit emits a `sveltekit:start` [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) on the `window` object once the app has hydrated. -- `sveltekit:start` — fired once the app has hydrated -- `sveltekit:navigation-start` — navigation has started -- `sveltekit:navigation-end` — navigation has ended - -You probably won't need to use these, but they can be useful in the context of (for example) integration tests. +You probably won't need to use it, but it can be useful in the context of (for example) integration tests. diff --git a/packages/adapter-auto/CHANGELOG.md b/packages/adapter-auto/CHANGELOG.md index d86e0f786211..a338ac67ad81 100644 --- a/packages/adapter-auto/CHANGELOG.md +++ b/packages/adapter-auto/CHANGELOG.md @@ -1,5 +1,29 @@ # @sveltejs/adapter-auto +## 1.0.0-next.27 + +### Patch Changes + +- Bumping versions again ([#4090](https://github.com/sveltejs/kit/pull/4090)) + +- Updated dependencies [[`69401762`](https://github.com/sveltejs/kit/commit/694017624ec8f1abaad7ebefb867bb065f7adf05)]: + - @sveltejs/adapter-cloudflare@1.0.0-next.14 + +## 1.0.0-next.26 + +### Patch Changes + +- Attempt to force @next version bump ([#4088](https://github.com/sveltejs/kit/pull/4088)) + +- Updated dependencies [[`46f44a61`](https://github.com/sveltejs/kit/commit/46f44a61624bed525e393e669da9b7db566a1e18)]: + - @sveltejs/adapter-cloudflare@1.0.0-next.13 + +## 1.0.0-next.25 + +### Patch Changes + +- Attempt to bump @next version of adapter-auto ([#4082](https://github.com/sveltejs/kit/pull/4082)) + ## 1.0.0-next.24 ### Patch Changes diff --git a/packages/adapter-auto/package.json b/packages/adapter-auto/package.json index acd5a54d3f10..a1b77e9798ae 100644 --- a/packages/adapter-auto/package.json +++ b/packages/adapter-auto/package.json @@ -1,6 +1,6 @@ { "name": "@sveltejs/adapter-auto", - "version": "1.0.0-next.24", + "version": "1.0.0-next.27", "repository": { "type": "git", "url": "https://github.com/sveltejs/kit", diff --git a/packages/adapter-cloudflare/CHANGELOG.md b/packages/adapter-cloudflare/CHANGELOG.md index 21a6a92653a2..ba1e7c11da74 100644 --- a/packages/adapter-cloudflare/CHANGELOG.md +++ b/packages/adapter-cloudflare/CHANGELOG.md @@ -1,5 +1,17 @@ # @sveltejs/adapter-cloudflare +## 1.0.0-next.14 + +### Patch Changes + +- Bumping versions again ([#4090](https://github.com/sveltejs/kit/pull/4090)) + +## 1.0.0-next.13 + +### Patch Changes + +- Attempt to force @next version bump ([#4088](https://github.com/sveltejs/kit/pull/4088)) + ## 1.0.0-next.12 ### Patch Changes diff --git a/packages/adapter-cloudflare/package.json b/packages/adapter-cloudflare/package.json index 8ea34245c606..c13ec7527c92 100644 --- a/packages/adapter-cloudflare/package.json +++ b/packages/adapter-cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "@sveltejs/adapter-cloudflare", - "version": "1.0.0-next.12", + "version": "1.0.0-next.14", "repository": { "type": "git", "url": "https://github.com/sveltejs/kit", diff --git a/packages/adapter-netlify/README.md b/packages/adapter-netlify/README.md index ef567b62d5f8..52979b7bc155 100644 --- a/packages/adapter-netlify/README.md +++ b/packages/adapter-netlify/README.md @@ -66,7 +66,6 @@ During compilation, redirect rules are automatically appended to your `_redirect [functions] directory = "functions" - node_bundler = "esbuild" ``` ## Changelog diff --git a/packages/adapter-netlify/src/handler.js b/packages/adapter-netlify/src/handler.js index fcf928145ccf..a81c53978d54 100644 --- a/packages/adapter-netlify/src/handler.js +++ b/packages/adapter-netlify/src/handler.js @@ -73,7 +73,8 @@ function split_headers(headers) { headers.forEach((value, key) => { if (key === 'set-cookie') { - m[key] = value.split(', '); + // @ts-expect-error (headers.raw() is non-standard) + m[key] = headers.raw()[key]; } else { h[key] = value; } diff --git a/packages/create-svelte/CHANGELOG.md b/packages/create-svelte/CHANGELOG.md index 50b5164d4838..5bbb2df72a45 100644 --- a/packages/create-svelte/CHANGELOG.md +++ b/packages/create-svelte/CHANGELOG.md @@ -1,5 +1,23 @@ # create-svelte +## 2.0.0-next.121 + +### Patch Changes + +- Add option to create integration tests with Playwright ([#4056](https://github.com/sveltejs/kit/pull/4056)) + +## 2.0.0-next.120 + +### Patch Changes + +- fix `@typescript-eslint/no-empty-interface` lint error when starting a new app with eslint ([#4077](https://github.com/sveltejs/kit/pull/4077)) + +## 2.0.0-next.119 + +### Patch Changes + +- [fix] update docs URL for App namespace interfaces ([#4042](https://github.com/sveltejs/kit/pull/4042)) + ## 2.0.0-next.118 ### Patch Changes diff --git a/packages/create-svelte/bin.js b/packages/create-svelte/bin.js index 6eadd82e9f70..8439db01fc31 100755 --- a/packages/create-svelte/bin.js +++ b/packages/create-svelte/bin.js @@ -92,6 +92,14 @@ async function main() { initial: false, active: 'Yes', inactive: 'No' + }, + { + type: 'toggle', + name: 'playwright', + message: 'Add Playwright for browser testing?', + initial: false, + active: 'Yes', + inactive: 'No' } ], { @@ -124,6 +132,11 @@ async function main() { console.log(cyan(' https://github.com/sveltejs/prettier-plugin-svelte#options')); } + if (options.playwright) { + console.log(bold('✔ Playwright')); + console.log(cyan(' https://playwright.dev')); + } + console.log('\nInstall community-maintained integrations:'); console.log(cyan(' https://github.com/svelte-add/svelte-adders')); diff --git a/packages/create-svelte/package.json b/packages/create-svelte/package.json index 35cbf938c79d..dafda96fb24f 100644 --- a/packages/create-svelte/package.json +++ b/packages/create-svelte/package.json @@ -1,6 +1,6 @@ { "name": "create-svelte", - "version": "2.0.0-next.118", + "version": "2.0.0-next.121", "repository": { "type": "git", "url": "https://github.com/sveltejs/kit", diff --git a/packages/create-svelte/scripts/update-template-repo-contents.js b/packages/create-svelte/scripts/update-template-repo-contents.js index 0cf14aa24880..54c93b629a8e 100644 --- a/packages/create-svelte/scripts/update-template-repo-contents.js +++ b/packages/create-svelte/scripts/update-template-repo-contents.js @@ -18,5 +18,6 @@ await create(repo, { template: 'default', eslint: false, typescript: false, - prettier: true + prettier: true, + playwright: false }); diff --git a/packages/create-svelte/shared/+playwright+default/tests/test.js b/packages/create-svelte/shared/+playwright+default/tests/test.js new file mode 100644 index 000000000000..c104fe8eb7ef --- /dev/null +++ b/packages/create-svelte/shared/+playwright+default/tests/test.js @@ -0,0 +1,6 @@ +import { expect, test } from '@playwright/test'; + +test('about page has expected h1', async ({ page }) => { + await page.goto('/about'); + expect(await page.textContent('h1')).toBe('About this app'); +}); diff --git a/packages/create-svelte/shared/+playwright+skeleton/tests/test.js b/packages/create-svelte/shared/+playwright+skeleton/tests/test.js new file mode 100644 index 000000000000..4e579377eed9 --- /dev/null +++ b/packages/create-svelte/shared/+playwright+skeleton/tests/test.js @@ -0,0 +1,6 @@ +import { expect, test } from '@playwright/test'; + +test('index page has expected h1', async ({ page }) => { + await page.goto('/'); + expect(await page.textContent('h1')).toBe('Welcome to SvelteKit'); +}); diff --git a/packages/create-svelte/shared/+playwright/package.json b/packages/create-svelte/shared/+playwright/package.json new file mode 100644 index 000000000000..0181674d4b01 --- /dev/null +++ b/packages/create-svelte/shared/+playwright/package.json @@ -0,0 +1,8 @@ +{ + "devDependencies": { + "@playwright/test": "^1.19.1" + }, + "scripts": { + "test": "playwright test" + } +} diff --git a/packages/create-svelte/shared/+playwright/playwright.config.js b/packages/create-svelte/shared/+playwright/playwright.config.js new file mode 100644 index 000000000000..448008a2f186 --- /dev/null +++ b/packages/create-svelte/shared/+playwright/playwright.config.js @@ -0,0 +1,9 @@ +/** @type {import('@playwright/test').PlaywrightTestConfig} */ +const config = { + webServer: { + command: 'npm run build && npm run preview', + port: 3000 + } +}; + +export default config; diff --git a/packages/create-svelte/templates/default/src/app.d.ts b/packages/create-svelte/templates/default/src/app.d.ts index 3ddb22d76c06..dec2abdb691e 100644 --- a/packages/create-svelte/templates/default/src/app.d.ts +++ b/packages/create-svelte/templates/default/src/app.d.ts @@ -1,15 +1,15 @@ /// -// See https://kit.svelte.dev/docs/typescript +// See https://kit.svelte.dev/docs/types#the-app-namespace // for information about these interfaces declare namespace App { interface Locals { userid: string; } - interface Platform {} + // interface Platform {} - interface Session {} + // interface Session {} - interface Stuff {} + // interface Stuff {} } diff --git a/packages/create-svelte/templates/skeleton/src/app.d.ts b/packages/create-svelte/templates/skeleton/src/app.d.ts index 7090ee710802..9cbf1c51cb74 100644 --- a/packages/create-svelte/templates/skeleton/src/app.d.ts +++ b/packages/create-svelte/templates/skeleton/src/app.d.ts @@ -1,13 +1,10 @@ /// -// See https://kit.svelte.dev/docs/typescript +// See https://kit.svelte.dev/docs/types#the-app-namespace // for information about these interfaces declare namespace App { - interface Locals {} - - interface Platform {} - - interface Session {} - - interface Stuff {} + // interface Locals {} + // interface Platform {} + // interface Session {} + // interface Stuff {} } diff --git a/packages/create-svelte/types/internal.d.ts b/packages/create-svelte/types/internal.d.ts index 8be632663fd4..115e9296ea6c 100644 --- a/packages/create-svelte/types/internal.d.ts +++ b/packages/create-svelte/types/internal.d.ts @@ -4,6 +4,7 @@ export type Options = { typescript: boolean; prettier: boolean; eslint: boolean; + playwright: boolean; }; export type File = { @@ -11,7 +12,13 @@ export type File = { contents: string; }; -export type Condition = 'eslint' | 'prettier' | 'typescript' | 'skeleton' | 'default'; +export type Condition = + | 'eslint' + | 'prettier' + | 'typescript' + | 'playwright' + | 'skeleton' + | 'default'; export type Common = { files: Array<{ diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md index 4edcf724f1ad..9e5cbf9ffa40 100644 --- a/packages/kit/CHANGELOG.md +++ b/packages/kit/CHANGELOG.md @@ -1,5 +1,35 @@ # @sveltejs/kit +## 1.0.0-next.285 + +### Patch Changes + +- [breaking] referer header sent by fetch in load matches page's referer header, not the page itself ([#4070](https://github.com/sveltejs/kit/pull/4070)) + +* [breaking] remove sveltekit:navigation-{start,end} events ([#4070](https://github.com/sveltejs/kit/pull/4070)) + +- [breaking] defer pushState until navigation occurs ([#4070](https://github.com/sveltejs/kit/pull/4070)) + +## 1.0.0-next.284 + +### Patch Changes + +- Disable meta http-equiv tags for static amp configuration ([#4073](https://github.com/sveltejs/kit/pull/4073)) + +* Ignore click event if url does not have origin (e.g. `mailto:`, `tel:`) ([#4072](https://github.com/sveltejs/kit/pull/4072)) + +## 1.0.0-next.283 + +### Patch Changes + +- create `__data.json` for pathnames with trailing slashes, including `/` ([#4066](https://github.com/sveltejs/kit/pull/4066)) + +## 1.0.0-next.282 + +### Patch Changes + +- [fix] remove private methods to make Safari 14.1 work ([#4054](https://github.com/sveltejs/kit/pull/4054)) + ## 1.0.0-next.281 ### Patch Changes diff --git a/packages/kit/package.json b/packages/kit/package.json index 9bc721c3ca6f..9d1d403be76c 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -1,6 +1,6 @@ { "name": "@sveltejs/kit", - "version": "1.0.0-next.281", + "version": "1.0.0-next.285", "repository": { "type": "git", "url": "https://github.com/sveltejs/kit", @@ -16,7 +16,7 @@ }, "devDependencies": { "@playwright/test": "^1.17.1", - "@rollup/plugin-replace": "^3.0.0", + "@rollup/plugin-replace": "^4.0.0", "@types/amphtml-validator": "^1.0.1", "@types/cookie": "^0.4.1", "@types/marked": "^4.0.1", diff --git a/packages/kit/src/core/dev/plugin.js b/packages/kit/src/core/dev/plugin.js index b0ed9f0d09e0..1ef00c758c9e 100644 --- a/packages/kit/src/core/dev/plugin.js +++ b/packages/kit/src/core/dev/plugin.js @@ -134,6 +134,24 @@ export async function create_plugin(config, cwd) { }; } + /** @param {Error} error */ + function fix_stack_trace(error) { + // TODO https://github.com/vitejs/vite/issues/7045 + + // ideally vite would expose ssrRewriteStacktrace, but + // in lieu of that, we can implement it ourselves. we + // don't want to mutate the error object, because + // the stack trace could be 'fixed' multiple times, + // and Vite will fix stack traces before we even + // see them if they occur during ssrLoadModule + const original = error.stack; + vite.ssrFixStacktrace(error); + const fixed = error.stack; + error.stack = original; + + return fixed; + } + update_manifest(); vite.watcher.on('add', update_manifest); @@ -238,13 +256,19 @@ export async function create_plugin(config, cwd) { dev: true, floc: config.kit.floc, get_stack: (error) => { - vite.ssrFixStacktrace(error); - return error.stack; + return fix_stack_trace(error); }, handle_error: (error, event) => { - vite.ssrFixStacktrace(error); hooks.handleError({ - error, + error: new Proxy(error, { + get: (target, property) => { + if (property === 'stack') { + return fix_stack_trace(error); + } + + return Reflect.get(target, property, target); + } + }), event, // TODO remove for 1.0 diff --git a/packages/kit/src/runtime/client/renderer.js b/packages/kit/src/runtime/client/renderer.js index cc8c98212c17..3c0db40a66eb 100644 --- a/packages/kit/src/runtime/client/renderer.js +++ b/packages/kit/src/runtime/client/renderer.js @@ -335,11 +335,10 @@ export class Renderer { }); } else { if (this.router) { - this.router.goto( - new URL(navigation_result.redirect, info.url).href, - { replaceState: true }, - [...chain, info.url.pathname] - ); + this.router.goto(new URL(navigation_result.redirect, info.url).href, {}, [ + ...chain, + info.url.pathname + ]); } else { location.href = new URL(navigation_result.redirect, location.href).href; } diff --git a/packages/kit/src/runtime/client/router.js b/packages/kit/src/runtime/client/router.js index 5573070e3afc..1f2351f35b70 100644 --- a/packages/kit/src/runtime/client/router.js +++ b/packages/kit/src/runtime/client/router.js @@ -17,6 +17,11 @@ try { // do nothing } +/** @param {number} index */ +function update_scroll_positions(index) { + scroll_positions[index] = scroll_state(); +} + function scroll_state() { return { x: pageXOffset, @@ -123,7 +128,7 @@ export class Router { addEventListener('visibilitychange', () => { if (document.visibilityState === 'hidden') { - this.#update_scroll_positions(); + update_scroll_positions(this.current_history_index); try { sessionStorage[SCROLL_KEY] = JSON.stringify(scroll_positions); @@ -175,6 +180,7 @@ export class Router { if (!a.href) return; + const is_svg_a_element = a instanceof SVGAElement; const url = get_href(a); const url_string = url.toString(); if (url_string === location.href) { @@ -182,6 +188,11 @@ export class Router { return; } + // Ignore if url does not have origin (e.g. `mailto:`, `tel:`.) + // MEMO: Without this condition, firefox will open mailer twice. + // See: https://github.com/sveltejs/kit/issues/4045 + if (!is_svg_a_element && url.origin === 'null') return; + // Ignore if tag has // 1. 'download' attribute // 2. 'rel' attribute includes external @@ -192,7 +203,7 @@ export class Router { } // Ignore if has a target - if (a instanceof SVGAElement ? a.target.baseVal : a.target) return; + if (is_svg_a_element ? a.target.baseVal : a.target) return; // Check if new url only differs by hash and use the browser default behavior in that case // This will ensure the `hashchange` event is fired @@ -203,7 +214,7 @@ export class Router { // clicking a hash link and those triggered by popstate this.hash_navigating = true; - this.#update_scroll_positions(); + update_scroll_positions(this.current_history_index); this.renderer.update_page_store(new URL(url.href)); return; @@ -262,10 +273,6 @@ export class Router { this.initialized = true; } - #update_scroll_positions() { - scroll_positions[this.current_history_index] = scroll_state(); - } - /** * Returns true if `url` has the same origin and basepath as the app * @param {URL} url @@ -414,24 +421,17 @@ export class Router { }); } - this.#update_scroll_positions(); + update_scroll_positions(this.current_history_index); accepted(); - if (!this.navigating) { - dispatchEvent(new CustomEvent('sveltekit:navigation-start')); - } this.navigating++; const pathname = normalize_path(url.pathname, this.trailing_slash); info.url = new URL(url.origin + pathname + url.search + url.hash); - if (details) { - const change = details.replaceState ? 0 : 1; - details.state['sveltekit:index'] = this.current_history_index += change; - history[details.replaceState ? 'replaceState' : 'pushState'](details.state, '', info.url); - } + const token = (this.navigating_token = {}); await this.renderer.handle_navigation(info, chain, false, { scroll, @@ -439,11 +439,18 @@ export class Router { }); this.navigating--; - if (!this.navigating) { - dispatchEvent(new CustomEvent('sveltekit:navigation-end')); + // navigation was aborted + if (this.navigating_token !== token) return; + if (!this.navigating) { const navigation = { from, to: url }; this.callbacks.after_navigate.forEach((fn) => fn(navigation)); } + + if (details) { + const change = details.replaceState ? 0 : 1; + details.state['sveltekit:index'] = this.current_history_index += change; + history[details.replaceState ? 'replaceState' : 'pushState'](details.state, '', info.url); + } } } diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index 194a00bdfcbf..841dc0f01779 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -139,8 +139,6 @@ export async function load_node({ } } - opts.headers.set('referer', event.url.href); - const resolved = resolve(event.url.pathname, requested.split('?')[0]); /** @type {Response} */ @@ -346,7 +344,7 @@ export async function load_node({ // generate __data.json files when prerendering if (shadow.body && state.prerender) { - const pathname = `${event.url.pathname}/__data.json`; + const pathname = `${event.url.pathname.replace(/\/$/, '')}/__data.json`; const dependency = { response: new Response(undefined), diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index a17133eb7d8e..433355bc57b3 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -278,7 +278,7 @@ export async function render_response({ } } - if (state.prerender) { + if (state.prerender && !options.amp) { const http_equiv = []; const csp_headers = csp.get_meta(); diff --git a/packages/kit/src/utils/url.js b/packages/kit/src/utils/url.js index 8029bf82472e..5f34ca4306d9 100644 --- a/packages/kit/src/utils/url.js +++ b/packages/kit/src/utils/url.js @@ -39,7 +39,7 @@ export function is_root_relative(path) { /** * @param {string} path - * @param {'always' | 'never' | 'ignore'} trailing_slash + * @param {import('types').TrailingSlash} trailing_slash */ export function normalize_path(path, trailing_slash) { if (path === '/' || trailing_slash === 'ignore') return path; diff --git a/packages/kit/test/ambient.d.ts b/packages/kit/test/ambient.d.ts index ea7820b66d38..ed7ec4e0f548 100644 --- a/packages/kit/test/ambient.d.ts +++ b/packages/kit/test/ambient.d.ts @@ -6,6 +6,7 @@ declare global { // used in tests oops: string; pageContext: any; + fulfil_navigation: (value: any) => void; } const goto: ( diff --git a/packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/anchor.svelte b/packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/anchor.svelte index 5f7ba71be5cb..be660c5859b2 100644 --- a/packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/anchor.svelte +++ b/packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/anchor.svelte @@ -8,16 +8,9 @@ }); -
They (don't) see me...
-
+
They (don't) see me...
+

The browser scrolls to me

-

I take precedence

+

I take precedence

- - diff --git a/packages/kit/test/apps/basics/src/routes/anchor/anchor.svelte b/packages/kit/test/apps/basics/src/routes/anchor/anchor.svelte index 2c4e5feeebf8..57406e6d3a09 100644 --- a/packages/kit/test/apps/basics/src/routes/anchor/anchor.svelte +++ b/packages/kit/test/apps/basics/src/routes/anchor/anchor.svelte @@ -1,11 +1,4 @@ -
They (don't) see me...
-
+
They (don't) see me...
+

The browser scrolls to me

- - 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.js index c3c604c268ca..488a97526280 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint.json.js +++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint.json.js @@ -1,4 +1,3 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ export function get() { throw new Error('nope'); } 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.js new file mode 100644 index 000000000000..bd8a8d6d6000 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/errors/init-error-endpoint.js @@ -0,0 +1,10 @@ +// @ts-expect-error +thisvariableisnotdefined; // eslint-disable-line + +export function get() { + return { + body: { + answer: 42 + } + }; +} 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 new file mode 100644 index 000000000000..7a04256ede3d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/cancellation/__layout.svelte @@ -0,0 +1,4 @@ + + +
a +b 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 new file mode 100644 index 000000000000..ec4ec8478ebb --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/cancellation/a.svelte @@ -0,0 +1,17 @@ + + +

this should not appear

\ 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/cancellation/b.svelte new file mode 100644 index 000000000000..51902b4da7a6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/cancellation/b.svelte @@ -0,0 +1 @@ +

b

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/routing/cancellation/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/cancellation/index.svelte new file mode 100644 index 000000000000..002578b92477 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/cancellation/index.svelte @@ -0,0 +1 @@ +

cancellation

\ No newline at end of file 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.svelte index 430c14300fb4..7dd3bb9fe6fa 100644 --- 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.svelte @@ -10,15 +10,8 @@
They (don't) see me scrollin'...
-
+
-
They (not) focusin'
- - +
They (not) focusin'
diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index c83e450369fb..47c769ce7c13 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -53,7 +53,7 @@ test.describe.parallel('a11y', () => { page.type('#input', 'bar'), page.waitForFunction(() => window.location.search === '?foo=bar') ]); - expect(await page.locator('#input')).toBeFocused(); + await expect(page.locator('#input')).toBeFocused(); } }); @@ -106,10 +106,10 @@ test.describe.parallel('beforeNavigate', () => { await page.goto('/before-navigate/prevent-navigation'); try { - await clicknav('[href="/before-navigate/a"]'); + await clicknav('[href="/before-navigate/a"]', { timeout: 1000 }); expect(false).toBe(true); } catch (/** @type {any} */ e) { - expect(e.message).toMatch('Timed out'); + expect(e.message).toMatch('page.waitForNavigation: Timeout 1000ms exceeded'); } expect(page.url()).toBe(baseURL + '/before-navigate/prevent-navigation'); @@ -230,17 +230,19 @@ test.describe('Scrolling', () => { test('url-supplied anchor is ignored with onMount() scrolling on navigation to page', async ({ page, clicknav, + javaScriptEnabled, in_view }) => { await page.goto('/anchor-with-manual-scroll'); await clicknav('[href="/anchor-with-manual-scroll/anchor#go-to-element"]'); - expect(await in_view('#abcde')).toBe(true); + if (javaScriptEnabled) expect(await in_view('#abcde')).toBe(true); + else expect(await in_view('#go-to-element')).toBe(true); }); test('app-supplied scroll and focus work on direct page load', async ({ page, in_view }) => { await page.goto('/use-action/focus-and-scroll'); expect(await in_view('#input')).toBe(true); - expect(await page.locator('#input')).toBeFocused(); + await expect(page.locator('#input')).toBeFocused(); }); test('app-supplied scroll and focus work on navigation to page', async ({ @@ -251,7 +253,7 @@ test.describe('Scrolling', () => { await page.goto('/use-action'); await clicknav('[href="/use-action/focus-and-scroll"]'); expect(await in_view('#input')).toBe(true); - expect(await page.locator('#input')).toBeFocused(); + await expect(page.locator('input')).toBeFocused(); }); test('scroll positions are recovered on reloading the page', async ({ page, back, app }) => { @@ -906,7 +908,7 @@ test.describe.parallel('Errors', () => { expect(lines[0]).toMatch('nope'); if (process.env.DEV) { - expect(lines[1]).toMatch('endpoint-shadow'); + expect(lines[1]).toMatch('endpoint-shadow.js:3:8'); } expect(res && res.status()).toBe(500); @@ -915,7 +917,7 @@ test.describe.parallel('Errors', () => { ); const contents = await page.textContent('#stack'); - const location = 'endpoint-shadow.js:1:8'; // TODO this is the wrong location, but i'm not going to open the sourcemap can of worms just now + const location = 'endpoint-shadow.js:3:8'; if (process.env.DEV) { expect(contents).toMatch(location); @@ -962,6 +964,14 @@ test.describe.parallel('Errors', () => { ); expect(await page.innerHTML('h1')).toBe('500'); }); + + // TODO re-enable this if https://github.com/vitejs/vite/issues/7046 is implemented + test.skip('error evaluating module', async ({ request }) => { + const response = await request.get('/errors/init-error-endpoint'); + + expect(response.status()).toBe(500); + expect(await response.text()).toMatch('thisvariableisnotdefined is not defined'); + }); }); test.describe.parallel('ETags', () => { @@ -1267,8 +1277,12 @@ test.describe.parallel('Load', () => { await clicknav('[href="/load/fetch-headers"]'); const json = /** @type {string} */ (await page.textContent('pre')); - expect(JSON.parse(json)).toEqual({ - referer: `${baseURL}/load/fetch-headers`, + const headers = JSON.parse(json); + + expect(headers).toEqual({ + // the referer will be the previous page in the client-side + // navigation case + referer: `${baseURL}/load`, // these headers aren't particularly useful, but they allow us to verify // that page headers are being forwarded 'sec-fetch-dest': javaScriptEnabled ? 'empty' : 'document', @@ -1629,13 +1643,17 @@ test.describe.parallel('searchParams', () => { }); test.describe.parallel('Redirects', () => { - test('redirect', async ({ page, clicknav }) => { + test('redirect', async ({ baseURL, page, clicknav, back }) => { await page.goto('/redirect'); await clicknav('[href="/redirect/a"]'); await page.waitForURL('/redirect/c'); expect(await page.textContent('h1')).toBe('c'); + expect(page.url()).toBe(`${baseURL}/redirect/c`); + + await back(); + expect(page.url()).toBe(`${baseURL}/redirect`); }); test('prevents redirect loops', async ({ baseURL, page, javaScriptEnabled }) => { @@ -2121,6 +2139,21 @@ test.describe.parallel('Routing', () => { await clicknav('[href="/static.json"]'); expect(await page.textContent('body')).toBe('"static file"\n'); }); + + test('navigation is cancelled upon subsequent navigation', async ({ + baseURL, + page, + clicknav + }) => { + await page.goto('/routing/cancellation'); + await page.click('[href="/routing/cancellation/a"]'); + await clicknav('[href="/routing/cancellation/b"]'); + + expect(await page.url()).toBe(`${baseURL}/routing/cancellation/b`); + + await page.evaluate('window.fulfil_navigation && window.fulfil_navigation()'); + expect(await page.url()).toBe(`${baseURL}/routing/cancellation/b`); + }); }); test.describe.parallel('Session', () => { diff --git a/packages/kit/test/prerendering/basics/src/routes/index.js b/packages/kit/test/prerendering/basics/src/routes/index.js new file mode 100644 index 000000000000..a566f067fabc --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/index.js @@ -0,0 +1,7 @@ +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 index 6e7c1e6ac83f..7c991db4005a 100644 --- a/packages/kit/test/prerendering/basics/src/routes/index.svelte +++ b/packages/kit/test/prerendering/basics/src/routes/index.svelte @@ -1 +1,6 @@ -

hello

+ + +

{message}

diff --git a/packages/kit/test/prerendering/basics/test/test.js b/packages/kit/test/prerendering/basics/test/test.js index eb07ec1cd2b1..03594e1bd1dd 100644 --- a/packages/kit/test/prerendering/basics/test/test.js +++ b/packages/kit/test/prerendering/basics/test/test.js @@ -63,9 +63,9 @@ test('loads a file with spaces in the filename', () => { assert.ok(content.includes('

answer: 42

'), content); }); -test('generates __data.json file for shadow endpoint', () => { - const json = read('shadowed-get/__data.json'); - assert.equal(json, JSON.stringify({ answer: 42 })); +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 })); }); test('does not prerender page with shadow endpoint with non-GET handler', () => { diff --git a/packages/kit/test/utils.d.ts b/packages/kit/test/utils.d.ts index 194880ad7948..aea4c91489ca 100644 --- a/packages/kit/test/utils.d.ts +++ b/packages/kit/test/utils.d.ts @@ -20,7 +20,7 @@ export const test: TestType< prefetchRoutes: (urls: string[]) => Promise; }; back: () => Promise; - clicknav: (selector: string) => Promise; + clicknav: (selector: string, options?: { timeout?: number }) => Promise; in_view: (selector: string) => Promise; read_errors: (href: string) => string; }, diff --git a/packages/kit/test/utils.js b/packages/kit/test/utils.js index b5e4bbfff4c8..9fb8f1cd077d 100644 --- a/packages/kit/test/utils.js +++ b/packages/kit/test/utils.js @@ -58,20 +58,6 @@ export const test = base.extend({ back: async ({ page, javaScriptEnabled }, use) => { use(async () => { if (javaScriptEnabled) { - await page.evaluate(() => { - window.navigated = new Promise((fulfil, reject) => { - const timeout = setTimeout(() => reject(new Error('Timed out')), 2000); - addEventListener( - 'sveltekit:navigation-end', - () => { - clearTimeout(timeout); - fulfil(); - }, - { once: true } - ); - }); - }); - await Promise.all([page.goBack(), page.evaluate(() => window.navigated)]); } else { return page.goBack().then(() => void 0); @@ -81,24 +67,13 @@ export const test = base.extend({ // @ts-expect-error clicknav: async ({ page, javaScriptEnabled }, use) => { - /** @param {string} selector */ - async function clicknav(selector) { + /** + * @param {string} selector + * @param {{ timeout: number }} options + */ + async function clicknav(selector, options) { if (javaScriptEnabled) { - await page.evaluate(() => { - window.navigated = new Promise((fulfil, reject) => { - const timeout = setTimeout(() => reject(new Error('Timed out')), 2000); - addEventListener( - 'sveltekit:navigation-end', - () => { - clearTimeout(timeout); - fulfil(); - }, - { once: true } - ); - }); - }); - - await Promise.all([page.click(selector), page.evaluate(() => window.navigated)]); + await Promise.all([page.click(selector), page.waitForNavigation(options)]); } else { await page.click(selector); } diff --git a/packages/kit/types/ambient.d.ts b/packages/kit/types/ambient.d.ts index 5cffcef510be..05ec5aff0f34 100644 --- a/packages/kit/types/ambient.d.ts +++ b/packages/kit/types/ambient.d.ts @@ -1,5 +1,3 @@ -/* eslint-disable import/no-duplicates */ - declare namespace App { interface Locals {} interface Platform {} @@ -107,7 +105,7 @@ declare module '$app/paths' { declare module '$app/stores' { import { Readable, Writable } from 'svelte/store'; - type Navigating = { from: URL; to: URL }; + import { Navigation, Page } from '@sveltejs/kit'; /** * A convenience function around `getContext` that returns `{ navigating, page, session }`. @@ -119,22 +117,17 @@ declare module '$app/stores' { session: Writable; updated: typeof updated; }; + /** * A readable store whose value contains page data. */ - export const page: Readable<{ - url: URL; - params: Record; - stuff: App.Stuff; - status: number; - error: Error | null; - }>; + export const page: Readable; /** * A readable store. * When navigating starts, its value is `{ from: URL, to: URL }` * When navigating finishes, its value reverts to `null`. */ - export const navigating: Readable; + export const navigating: Readable; /** * A writable store whose initial value is whatever was returned from `getSession`. * It can be written to, but this will not cause changes to persist on the server — this is something you must implement yourself. diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index d2a2a0f2e0e7..6b34f432d18a 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -1,6 +1,8 @@ /// /// +import './ambient'; + import { CompileOptions } from 'svelte/types/compiler/interfaces'; import './ambient'; @@ -181,7 +183,7 @@ export namespace Csp { type BaseSource = 'self' | 'unsafe-eval' | 'unsafe-hashes' | 'unsafe-inline' | 'none'; type CryptoSource = `${'nonce' | 'sha256' | 'sha384' | 'sha512'}-${string}`; type FrameSource = HostSource | SchemeSource | 'self' | 'none'; - type HostNameScheme = `${string}.${string}` | `localhost`; + type HostNameScheme = `${string}.${string}` | 'localhost'; type HostSource = `${HostProtocolSchemes}${HostNameScheme}${PortScheme}`; type HostProtocolSchemes = `${string}://` | ''; type HttpDelineator = '/' | '?' | '#' | '\\'; @@ -338,8 +340,21 @@ interface Logger { type MaybePromise = T | Promise; +export interface Navigation { + from: URL; + to: URL; +} + type Only = { [P in keyof T]: T[P] } & { [P in Exclude]?: never }; +export interface Page = Record> { + url: URL; + params: Params; + stuff: App.Stuff; + status: number; + error: Error | null; +} + export interface Prerendered { pages: Map< string, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f886f30f1375..185609670d25 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -225,7 +225,7 @@ importers: packages/kit: specifiers: '@playwright/test': ^1.17.1 - '@rollup/plugin-replace': ^3.0.0 + '@rollup/plugin-replace': ^4.0.0 '@sveltejs/vite-plugin-svelte': ^1.0.0-next.32 '@types/amphtml-validator': ^1.0.1 '@types/cookie': ^0.4.1 @@ -261,7 +261,7 @@ importers: vite: 2.8.0 devDependencies: '@playwright/test': 1.17.1 - '@rollup/plugin-replace': 3.0.0_rollup@2.60.2 + '@rollup/plugin-replace': 4.0.0_rollup@2.60.2 '@types/amphtml-validator': 1.0.1 '@types/cookie': 0.4.1 '@types/marked': 4.0.1 @@ -1402,8 +1402,8 @@ packages: rollup: 2.60.2 dev: true - /@rollup/plugin-replace/3.0.0_rollup@2.60.2: - resolution: {integrity: sha512-3c7JCbMuYXM4PbPWT4+m/4Y6U60SgsnDT/cCyAyUKwFHg7pTSfsSQzIpETha3a3ig6OdOKzZz87D9ZXIK3qsDg==} + /@rollup/plugin-replace/4.0.0_rollup@2.60.2: + resolution: {integrity: sha512-+rumQFiaNac9y64OHtkHGmdjm7us9bo1PlbgQfdihQtuNxzjpaB064HbRnewUOggLQxVCCyINfStkgmBeQpv1g==} peerDependencies: rollup: ^1.20.0 || ^2.0.0 dependencies: diff --git a/sites/kit.svelte.dev/src/lib/docs/client/docs.css b/sites/kit.svelte.dev/src/lib/docs/client/docs.css index 9d53baaccca7..db33eeb29273 100644 --- a/sites/kit.svelte.dev/src/lib/docs/client/docs.css +++ b/sites/kit.svelte.dev/src/lib/docs/client/docs.css @@ -55,6 +55,7 @@ bottom: 0.3rem; opacity: 0; transition: opacity 0.2s; + user-select: none; } .content h2 .anchor { @@ -160,6 +161,7 @@ .content table { margin: 0; margin-bottom: 2em; + max-width: var(--linemax); } .content section { diff --git a/sites/kit.svelte.dev/src/routes/content.json.js b/sites/kit.svelte.dev/src/routes/content.json.js index 77d0bab96696..ad1866f24467 100644 --- a/sites/kit.svelte.dev/src/routes/content.json.js +++ b/sites/kit.svelte.dev/src/routes/content.json.js @@ -103,14 +103,10 @@ function plaintext(markdown) { listitem: block, checkbox: block, paragraph: (text) => `${text}\n\n`, - table: () => { - throw new Error('TODO implement tables'); - }, - tablerow: () => { - throw new Error('TODO implement tables'); - }, - tablecell: () => { - throw new Error('TODO implement tables'); + table: block, + tablerow: block, + tablecell: (text, opts) => { + return text + ' '; }, strong: inline, em: inline,