sveltejs / kit

web development, streamlined
https://kit.svelte.dev
MIT License
18.42k stars 1.88k forks source link

`handleMissingId` should treat `#top` as a valid ID #12367

Open josh-collinsworth opened 2 months ago

josh-collinsworth commented 2 months ago

Describe the bug

The ID #top is a perfectly valid href attribute, which can be used to scroll a user to the top of an HTML document. Per the HTML spec:

If decodedFragment is an ASCII case-insensitive match for the string top, then return the top of the document.

This is also confirmed by MDN.

However, at build time, an anchor tag with href="#top" will throw an error:

Error: The following pages contain links to /foo/bar#top, but no element with id="top" exists on /foo/bar - see the `handleMissingId` option in https://kit.svelte.dev/docs/configuration#prerender for more info:

This shouldn't be the case, as #top is, again, a valid HTML href value.

Further, users shouldn't be forced to adjust their compiler options simply to be able to use something that's in the spec.

Reproduction

Run a static build on a SvelteKit site with a <a href="#top">Top</a> link present

Logs

9:24:57 AM: Failed during stage 'building site': Build script returned non-zero exit code: 2 (https://ntl.fyi/exit-code-2)
9:24:57 AM: file:///opt/build/repo/node_modules/@sveltejs/kit/src/core/postbuild/prerender.js:53
9:24:57 AM:                     throw new Error(format(details));
9:24:57 AM:                           ^
9:24:57 AM: Error: The following pages contain links to /blog/antiquated-react#top, but no element with id="top" exists on /blog/antiquated-react - see the `handleMissingId` option in https://kit.svelte.dev/docs/configuration#prerender for more info:
9:24:57 AM:   - /blog/antiquated-react
9:24:57 AM:     at file:///opt/build/repo/node_modules/@sveltejs/kit/src/core/postbuild/prerender.js:53:12
9:24:57 AM:     at prerender (file:///opt/build/repo/node_modules/@sveltejs/kit/src/core/postbuild/prerender.js:398:4)
9:24:57 AM:     at async process.<anonymous> (file:///opt/build/repo/node_modules/@sveltejs/kit/src/utils/fork.js:25:17)
9:24:57 AM: Node.js v19.3.0
9:24:57 AM: [vite-plugin-sveltekit-compile] Failed with code 1
9:24:57 AM: error during build:
9:24:57 AM: Error: Failed with code 1
9:24:57 AM:     at ChildProcess.<anonymous> (file:///opt/build/repo/node_modules/@sveltejs/kit/src/utils/fork.js:68:13)
9:24:57 AM:     at ChildProcess.emit (node:events:513:28)
9:24:57 AM:     at ChildProcess._handle.onexit (node:internal/child_process:293:12)
9:24:57 AM: ​
9:24:57 AM: "build.command" failed                                        
9:24:57 AM: ────────────────────────────────────────────────────────────────

System Info

System:
    OS: macOS 14.5
    CPU: (12) arm64 Apple M2 Pro
    Memory: 158.53 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 19.3.0 - ~/.nvm/versions/node/v19.3.0/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v19.3.0/bin/yarn
    npm: 9.2.0 - ~/.nvm/versions/node/v19.3.0/bin/npm
  Browsers:
    Chrome: 126.0.6478.61
    Edge: 126.0.2592.61
    Safari: 17.5

Severity

annoyance

Additional Information

No response

Nirmit4604 commented 2 months ago

1st Approach (Add a Dummy Element with id="top")

Open the affected Svelte component file, Add a div with id="top" at the top of the file.

<!-- src/routes/foo/bar.svelte -->
<div id="top"></div>
--> Rest of your page content 

1. 

<a href="#top">Back to top</a>

2nd Approach (Adjust SvelteKit Prerender Configuration)

Open your svelte.config.js file, Modify the prerender configuration to ignore missing IDs.

import adapter from '@sveltejs/adapter-static';

export default {
  kit: {
    adapter: adapter(),
    prerender: {
      handleMissingId: 'ignore', // Other options: 'fail', 'warn'
    },
  },
};
josh-collinsworth commented 2 months ago

@Nirmit4604 I'm aware of both of these workarounds; however, neither should be needed because the code is valid to begin with. It is being inappropriately treated as an error, when it should not be. Developers shouldn't need to embrace workarounds when there's nothing wrong with the code in the first place.

Besides, it's entirely conceivable a developer might want #top to work, but not want other missing IDs to pass.

Nirmit4604 commented 2 months ago

So can we create a custom preprocessor to handle #top specifically and then integrate it into your svelte.config.js file?

The handleTopFragment preprocessor specifically looks for href="#top" and replaces it with a JavaScript-based scroll handle. This ensures that #top is handled correctly, while the prerender configuration (handleMissingId: 'warn') continues to apply to other missing IDs.