From ca5d5ecc957678d3b1fb98011818fe8fd3492a26 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Mon, 18 Dec 2023 11:56:19 +0800 Subject: [PATCH] fix: prevent endless SPA 404 loop (#11354) --- .changeset/angry-lamps-deny.md | 5 ++ packages/kit/src/runtime/client/client.js | 19 +++-- packages/kit/src/runtime/control.js | 1 + .../runtime/server/page/respond_with_error.js | 2 +- packages/kit/test/apps/no-ssr/package.json | 24 ++++++ .../kit/test/apps/no-ssr/playwright.config.js | 1 + packages/kit/test/apps/no-ssr/src/app.html | 12 +++ .../test/apps/no-ssr/src/routes/+layout.js | 1 + .../apps/no-ssr/src/routes/+layout.server.js | 0 .../apps/no-ssr/src/routes/+layout.svelte | 7 ++ .../test/apps/no-ssr/src/routes/+page.svelte | 0 .../kit/test/apps/no-ssr/static/favicon.png | Bin 0 -> 1571 bytes .../kit/test/apps/no-ssr/svelte.config.js | 6 ++ packages/kit/test/apps/no-ssr/test/test.js | 15 ++++ packages/kit/test/apps/no-ssr/tsconfig.json | 14 ++++ packages/kit/test/apps/no-ssr/vite.config.js | 18 +++++ pnpm-lock.yaml | 70 +++++++++++++----- 17 files changed, 167 insertions(+), 28 deletions(-) create mode 100644 .changeset/angry-lamps-deny.md create mode 100644 packages/kit/test/apps/no-ssr/package.json create mode 100644 packages/kit/test/apps/no-ssr/playwright.config.js create mode 100644 packages/kit/test/apps/no-ssr/src/app.html create mode 100644 packages/kit/test/apps/no-ssr/src/routes/+layout.js create mode 100644 packages/kit/test/apps/no-ssr/src/routes/+layout.server.js create mode 100644 packages/kit/test/apps/no-ssr/src/routes/+layout.svelte create mode 100644 packages/kit/test/apps/no-ssr/src/routes/+page.svelte create mode 100644 packages/kit/test/apps/no-ssr/static/favicon.png create mode 100644 packages/kit/test/apps/no-ssr/svelte.config.js create mode 100644 packages/kit/test/apps/no-ssr/test/test.js create mode 100644 packages/kit/test/apps/no-ssr/tsconfig.json create mode 100644 packages/kit/test/apps/no-ssr/vite.config.js diff --git a/.changeset/angry-lamps-deny.md b/.changeset/angry-lamps-deny.md new file mode 100644 index 000000000000..5579c4fb2825 --- /dev/null +++ b/.changeset/angry-lamps-deny.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/kit": patch +--- + +fix: prevent endless SPA 404 loop diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index 1c5e86cd19a6..80fa5c07e116 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -2118,16 +2118,21 @@ async function load_data(url, invalid) { const res = await native_fetch(data_url.href); - // if `__data.json` doesn't exist or the server has an internal error, - // fallback to native navigation so we avoid parsing the HTML error page as a JSON - if (res.headers.get('content-type')?.includes('text/html')) { - await native_navigation(url); - } - if (!res.ok) { // error message is a JSON-stringified string which devalue can't handle at the top level // turn it into a HttpError to not call handleError on the client again (was already handled on the server) - throw new HttpError(res.status, await res.json()); + // if `__data.json` doesn't exist or the server has an internal error, + // avoid parsing the HTML error page as a JSON + /** @type {string | undefined} */ + let message; + if (res.headers.get('content-type')?.includes('application/json')) { + message = await res.json(); + } else if (res.status === 404) { + message = 'Not Found'; + } else if (res.status === 500) { + message = 'Internal Error'; + } + throw new HttpError(res.status, message); } // TODO: fix eslint error / figure out if it actually applies to our situation diff --git a/packages/kit/src/runtime/control.js b/packages/kit/src/runtime/control.js index b55430e88820..64737a9cff1d 100644 --- a/packages/kit/src/runtime/control.js +++ b/packages/kit/src/runtime/control.js @@ -33,6 +33,7 @@ export class Redirect { /** * An error that was thrown from within the SvelteKit runtime that is not fatal and doesn't result in a 500, such as a 404. * `SvelteKitError` goes through `handleError`. + * @extends Error */ export class SvelteKitError extends Error { /** 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 e320b01f04a4..121ebc5406f2 100644 --- a/packages/kit/src/runtime/server/page/respond_with_error.js +++ b/packages/kit/src/runtime/server/page/respond_with_error.js @@ -86,7 +86,7 @@ export async function respond_with_error({ state, page_config: { ssr, - csr: get_option([default_layout], 'csr') ?? true + csr }, status, error: await handle_error_and_jsonify(event, options, error), diff --git a/packages/kit/test/apps/no-ssr/package.json b/packages/kit/test/apps/no-ssr/package.json new file mode 100644 index 000000000000..269ea45c1d8c --- /dev/null +++ b/packages/kit/test/apps/no-ssr/package.json @@ -0,0 +1,24 @@ +{ + "name": "test-no-ssr", + "private": true, + "version": "0.0.1", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && tsc && svelte-check", + "test": "pnpm test:dev && pnpm test:build", + "test:dev": "cross-env DEV=true playwright test", + "test:build": "playwright test" + }, + "devDependencies": { + "@sveltejs/kit": "workspace:^", + "@sveltejs/vite-plugin-svelte": "^3.0.1", + "cross-env": "^7.0.3", + "svelte": "^4.2.8", + "svelte-check": "^3.6.2", + "typescript": "^5.3.3", + "vite": "^5.0.8" + }, + "type": "module" +} diff --git a/packages/kit/test/apps/no-ssr/playwright.config.js b/packages/kit/test/apps/no-ssr/playwright.config.js new file mode 100644 index 000000000000..33d36b651014 --- /dev/null +++ b/packages/kit/test/apps/no-ssr/playwright.config.js @@ -0,0 +1 @@ +export { config as default } from '../../utils.js'; diff --git a/packages/kit/test/apps/no-ssr/src/app.html b/packages/kit/test/apps/no-ssr/src/app.html new file mode 100644 index 000000000000..79d946ed86a3 --- /dev/null +++ b/packages/kit/test/apps/no-ssr/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/packages/kit/test/apps/no-ssr/src/routes/+layout.js b/packages/kit/test/apps/no-ssr/src/routes/+layout.js new file mode 100644 index 000000000000..a3d15781a772 --- /dev/null +++ b/packages/kit/test/apps/no-ssr/src/routes/+layout.js @@ -0,0 +1 @@ +export const ssr = false; diff --git a/packages/kit/test/apps/no-ssr/src/routes/+layout.server.js b/packages/kit/test/apps/no-ssr/src/routes/+layout.server.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/kit/test/apps/no-ssr/src/routes/+layout.svelte b/packages/kit/test/apps/no-ssr/src/routes/+layout.svelte new file mode 100644 index 000000000000..5e1f1fed86c2 --- /dev/null +++ b/packages/kit/test/apps/no-ssr/src/routes/+layout.svelte @@ -0,0 +1,7 @@ + + + diff --git a/packages/kit/test/apps/no-ssr/src/routes/+page.svelte b/packages/kit/test/apps/no-ssr/src/routes/+page.svelte new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/kit/test/apps/no-ssr/static/favicon.png b/packages/kit/test/apps/no-ssr/static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..825b9e65af7c104cfb07089bb28659393b4f2097 GIT binary patch literal 1571 zcmV+;2Hg3HP)Px)-AP12RCwC$UE6KzI1p6{F2N z1VK2vi|pOpn{~#djwYcWXTI_im_u^TJgMZ4JMOsSj!0ma>B?-(Hr@X&W@|R-$}W@Z zgj#$x=!~7LGqHW?IO8+*oE1MyDp!G=L0#^lUx?;!fXv@l^6SvTnf^ac{5OurzC#ZMYc20lI%HhX816AYVs1T3heS1*WaWH z%;x>)-J}YB5#CLzU@GBR6sXYrD>Vw(Fmt#|JP;+}<#6b63Ike{Fuo!?M{yEffez;| zp!PfsuaC)>h>-AdbnwN13g*1LowNjT5?+lFVd#9$!8Z9HA|$*6dQ8EHLu}U|obW6f z2%uGv?vr=KNq7YYa2Roj;|zooo<)lf=&2yxM@e`kM$CmCR#x>gI>I|*Ubr({5Y^rb zghxQU22N}F51}^yfDSt786oMTc!W&V;d?76)9KXX1 z+6Okem(d}YXmmOiZq$!IPk5t8nnS{%?+vDFz3BevmFNgpIod~R{>@#@5x9zJKEHLHv!gHeK~n)Ld!M8DB|Kfe%~123&Hz1Z(86nU7*G5chmyDe ziV7$pB7pJ=96hpxHv9rCR29%bLOXlKU<_13_M8x)6;P8E1Kz6G<&P?$P^%c!M5`2` zfY2zg;VK5~^>TJGQzc+33-n~gKt{{of8GzUkWmU110IgI0DLxRIM>0US|TsM=L|@F z0Bun8U!cRB7-2apz=y-7*UxOxz@Z0)@QM)9wSGki1AZ38ceG7Q72z5`i;i=J`ILzL z@iUO?SBBG-0cQuo+an4TsLy-g-x;8P4UVwk|D8{W@U1Zi z!M)+jqy@nQ$p?5tsHp-6J304Q={v-B>66$P0IDx&YT(`IcZ~bZfmn11#rXd7<5s}y zBi9eim&zQc0Dk|2>$bs0PnLmDfMP5lcXRY&cvJ=zKxI^f0%-d$tD!`LBf9^jMSYUA zI8U?CWdY@}cRq6{5~y+)#h1!*-HcGW@+gZ4B};0OnC~`xQOyH19z*TA!!BJ%9s0V3F?CAJ{hTd#*tf+ur-W9MOURF-@B77_-OshsY}6 zOXRY=5%C^*26z?l)1=$bz30!so5tfABdSYzO+H=CpV~aaUefmjvfZ3Ttu9W&W3Iu6 zROlh0MFA5h;my}8lB0tAV-Rvc2Zs_CCSJnx@d`**$idgy-iMob4dJWWw|21b4NB=LfsYp0Aeh{Ov)yztQi;eL4y5 zMi>8^SzKqk8~k?UiQK^^-5d8c%bV?$F8%X~czyiaKCI2=UH !javaScriptEnabled); + +test.describe.configure({ mode: 'parallel' }); + +test('navigating to a non-existent route renders the default error page', async ({ page }) => { + test.setTimeout(3000); + await page.goto('/non-existent-route'); + await page.waitForLoadState('networkidle'); + expect(await page.textContent('h1')).toBe('404'); +}); diff --git a/packages/kit/test/apps/no-ssr/tsconfig.json b/packages/kit/test/apps/no-ssr/tsconfig.json new file mode 100644 index 000000000000..f7ebdb5e4e7c --- /dev/null +++ b/packages/kit/test/apps/no-ssr/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "noEmit": true, + "paths": { + "@sveltejs/kit": ["../../../types"], + "types": ["../../../types/internal"] + }, + "resolveJsonModule": true + }, + "extends": "./.svelte-kit/tsconfig.json" +} diff --git a/packages/kit/test/apps/no-ssr/vite.config.js b/packages/kit/test/apps/no-ssr/vite.config.js new file mode 100644 index 000000000000..69200cdb7cd8 --- /dev/null +++ b/packages/kit/test/apps/no-ssr/vite.config.js @@ -0,0 +1,18 @@ +import * as path from 'node:path'; +import { sveltekit } from '@sveltejs/kit/vite'; + +/** @type {import('vite').UserConfig} */ +const config = { + build: { + minify: false + }, + clearScreen: false, + plugins: [sveltekit()], + server: { + fs: { + allow: [path.resolve('../../../src')] + } + } +}; + +export default config; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d162085c3d14..780c9619c9e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -592,6 +592,30 @@ importers: specifier: ^5.0.8 version: 5.0.8(@types/node@18.19.3)(lightningcss@1.22.1) + packages/kit/test/apps/no-ssr: + devDependencies: + '@sveltejs/kit': + specifier: workspace:^ + version: link:../../.. + '@sveltejs/vite-plugin-svelte': + specifier: ^3.0.1 + version: 3.0.1(svelte@4.2.8)(vite@5.0.8) + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + svelte: + specifier: ^4.2.8 + version: 4.2.8 + svelte-check: + specifier: ^3.6.2 + version: 3.6.2(postcss@8.4.32)(svelte@4.2.8) + typescript: + specifier: ^5.3.3 + version: 5.3.3 + vite: + specifier: ^5.0.8 + version: 5.0.8(@types/node@18.19.3)(lightningcss@1.22.1) + packages/kit/test/apps/options: devDependencies: '@sveltejs/kit': @@ -2212,6 +2236,32 @@ packages: typescript: 5.3.3 dev: true + /@sveltejs/kit@2.0.1(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.8): + resolution: {integrity: sha512-Pu/YJ5pPy8UgC1LlrlGpL7UK25CKganlLqqO7oYdFGt7eSa+Y2ouJomwiGHatV3uHvVOPGEkcb4O4QtyZQDuGw==} + engines: {node: '>=18.13'} + hasBin: true + requiresBuild: true + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^3.0.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.3 + dependencies: + '@sveltejs/vite-plugin-svelte': 3.0.1(svelte@4.2.8)(vite@5.0.8) + '@types/cookie': 0.6.0 + cookie: 0.6.0 + devalue: 4.3.2 + esm-env: 1.0.0 + kleur: 4.1.5 + magic-string: 0.30.5 + mrmime: 1.0.1 + sade: 1.8.1 + set-cookie-parser: 2.6.0 + sirv: 2.0.3 + svelte: 4.2.8 + tiny-glob: 0.2.9 + vite: 5.0.8(@types/node@18.19.3)(lightningcss@1.22.1) + dev: false + /@sveltejs/site-kit@6.0.0-next.59(@sveltejs/kit@packages+kit)(svelte@4.2.8): resolution: {integrity: sha512-nAUCuunhN0DmurQBxbsauqvdvv4mL0F/Aluxq0hFf6gB3iSn9WdaUZdPMXoujy+8cy+m6UvKuyhkgApZhmOLvw==} peerDependencies: @@ -2238,7 +2288,6 @@ packages: vite: 5.0.8(@types/node@18.19.3)(lightningcss@1.22.1) transitivePeerDependencies: - supports-color - dev: true /@sveltejs/vite-plugin-svelte@3.0.1(svelte@4.2.8)(vite@5.0.8): resolution: {integrity: sha512-CGURX6Ps+TkOovK6xV+Y2rn8JKa8ZPUHPZ/NKgCxAmgBrXReavzFl8aOSCj3kQ1xqT7yGJj53hjcV/gqwDAaWA==} @@ -2258,7 +2307,6 @@ packages: vitefu: 0.2.5(vite@5.0.8) transitivePeerDependencies: - supports-color - dev: true /@svitejs/changesets-changelog-github-compact@1.1.0: resolution: {integrity: sha512-qhUGGDHcpbY2zpjW3SwqchuW8J/5EzlPFud7xNntHKA7f3a/mx5+g+ruJKFHSAiVZYo30PALt+AyhmPUNKH/Og==} @@ -2330,7 +2378,6 @@ packages: resolution: {integrity: sha512-k5fggr14DwAytoA/t8rPrIz++lXK7/DqckthCmoZOKNsEbJkId4Z//BqgApXBUGrGddrigYa1oqheo/7YmW4rg==} dependencies: undici-types: 5.26.5 - dev: true /@types/normalize-package-data@2.4.4: resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -3237,7 +3284,6 @@ packages: resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} engines: {node: '>=0.10'} hasBin: true - dev: true /detect-libc@2.0.2: resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} @@ -4389,7 +4435,6 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true - dev: true optional: true /lightningcss-darwin-x64@1.22.1: @@ -4398,7 +4443,6 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true - dev: true optional: true /lightningcss-freebsd-x64@1.22.1: @@ -4407,7 +4451,6 @@ packages: cpu: [x64] os: [freebsd] requiresBuild: true - dev: true optional: true /lightningcss-linux-arm-gnueabihf@1.22.1: @@ -4416,7 +4459,6 @@ packages: cpu: [arm] os: [linux] requiresBuild: true - dev: true optional: true /lightningcss-linux-arm64-gnu@1.22.1: @@ -4425,7 +4467,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: true optional: true /lightningcss-linux-arm64-musl@1.22.1: @@ -4434,7 +4475,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: true optional: true /lightningcss-linux-x64-gnu@1.22.1: @@ -4443,7 +4483,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: true optional: true /lightningcss-linux-x64-musl@1.22.1: @@ -4452,7 +4491,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: true optional: true /lightningcss-win32-x64-msvc@1.22.1: @@ -4461,7 +4499,6 @@ packages: cpu: [x64] os: [win32] requiresBuild: true - dev: true optional: true /lightningcss@1.22.1: @@ -4479,7 +4516,6 @@ packages: lightningcss-linux-x64-gnu: 1.22.1 lightningcss-linux-x64-musl: 1.22.1 lightningcss-win32-x64-msvc: 1.22.1 - dev: true /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} @@ -4769,7 +4805,6 @@ packages: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - dev: true /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -5127,7 +5162,6 @@ packages: nanoid: 3.3.7 picocolors: 1.0.0 source-map-js: 1.0.2 - dev: true /preferred-pm@3.1.2: resolution: {integrity: sha512-nk7dKrcW8hfCZ4H6klWcdRknBOXWzNQByJ0oJyX97BOupsYD+FzLS4hflgEu/uPUEHZCuRfMxzCBsuWd7OzT8Q==} @@ -5829,7 +5863,6 @@ packages: svelte: ^3.19.0 || ^4.0.0 dependencies: svelte: 4.2.8 - dev: true /svelte-local-storage-store@0.6.4(svelte@4.2.8): resolution: {integrity: sha512-45WoY2vSGPQM1sIQJ9jTkPPj20hYeqm+af6mUGRFSPP5WglZf36YYoZqwmZZ8Dt/2SU8lem+BTA8/Z/8TkqNLg==} @@ -6167,7 +6200,6 @@ packages: /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - dev: true /universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} @@ -6291,7 +6323,6 @@ packages: rollup: 4.8.0 optionalDependencies: fsevents: 2.3.3 - dev: true /vitefu@0.2.5(vite@5.0.8): resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} @@ -6302,7 +6333,6 @@ packages: optional: true dependencies: vite: 5.0.8(@types/node@18.19.3)(lightningcss@1.22.1) - dev: true /vitest@1.0.4(@types/node@18.19.3)(lightningcss@1.22.1): resolution: {integrity: sha512-s1GQHp/UOeWEo4+aXDOeFBJwFzL6mjycbQwwKWX2QcYfh/7tIerS59hWQ20mxzupTJluA2SdwiBuWwQHH67ckg==}