diff --git a/.changeset/seven-teachers-tell.md b/.changeset/seven-teachers-tell.md new file mode 100644 index 000000000000..29c1a75dbd57 --- /dev/null +++ b/.changeset/seven-teachers-tell.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: throw more helpful error when encoding uri fails during prerendering diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index 515d815e3303..cdfaacb0d407 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -4,7 +4,7 @@ import { pathToFileURL } from 'node:url'; import { installPolyfills } from '../../exports/node/polyfills.js'; import { mkdirp, posixify, walk } from '../../utils/filesystem.js'; import { should_polyfill } from '../../utils/platform.js'; -import { is_root_relative, resolve } from '../../utils/url.js'; +import { decode_uri, is_root_relative, resolve } from '../../utils/url.js'; import { escape_html_attr } from '../../utils/escape.js'; import { logger } from '../utils.js'; import { load_config } from '../config/index.js'; @@ -207,7 +207,7 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) { // this seems circuitous, but using new URL allows us to not care // whether dependency_path is encoded or not const encoded_dependency_path = new URL(dependency_path, 'http://localhost').pathname; - const decoded_dependency_path = decodeURI(encoded_dependency_path); + const decoded_dependency_path = decode_uri(encoded_dependency_path); const headers = Object.fromEntries(result.response.headers); @@ -215,7 +215,7 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) { if (prerender) { const encoded_route_id = headers['x-sveltekit-routeid']; if (encoded_route_id != null) { - const route_id = decodeURI(encoded_route_id); + const route_id = decode_uri(encoded_route_id); const existing_value = prerender_map.get(route_id); if (existing_value !== 'auto') { prerender_map.set(route_id, prerender === 'true' ? true : 'auto'); @@ -257,7 +257,7 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) { } if (hash) { - const key = decodeURI(pathname + hash); + const key = decode_uri(pathname + hash); if (!expected_hashlinks.has(key)) { expected_hashlinks.set(key, new Set()); @@ -266,7 +266,7 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) { /** @type {Set} */ (expected_hashlinks.get(key)).add(decoded); } - enqueue(decoded, decodeURI(pathname), pathname); + enqueue(decoded, decode_uri(pathname), pathname); } } } @@ -293,7 +293,7 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) { if (written.has(file)) return; const encoded_route_id = response.headers.get('x-sveltekit-routeid'); - const route_id = encoded_route_id != null ? decodeURI(encoded_route_id) : null; + const route_id = encoded_route_id != null ? decode_uri(encoded_route_id) : null; if (route_id !== null) prerendered_routes.add(route_id); if (response_type === REDIRECT) { @@ -302,7 +302,7 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) { if (location) { const resolved = resolve(encoded, location); if (is_root_relative(resolved)) { - enqueue(decoded, decodeURI(resolved), resolved); + enqueue(decoded, decode_uri(resolved), resolved); } if (!headers['x-sveltekit-normalize']) { diff --git a/packages/kit/src/utils/url.js b/packages/kit/src/utils/url.js index d0c917b2bb64..239dace8b089 100644 --- a/packages/kit/src/utils/url.js +++ b/packages/kit/src/utils/url.js @@ -75,6 +75,21 @@ export function decode_params(params) { return params; } +/** + * The error when a URL is malformed is not very helpful, so we augment it with the URI + * @param {string} uri + */ +export function decode_uri(uri) { + try { + return decodeURI(uri); + } catch (e) { + if (e instanceof Error) { + e.message = `Failed to decode URI: ${uri}\n` + e.message; + } + throw e; + } +} + /** * URL properties that could change during the lifetime of the page, * which excludes things like `origin`