kit icon indicating copy to clipboard operation
kit copied to clipboard

Base hrefs are not supported in prerendering

Open tontonsb opened this issue 2 years ago • 0 comments

Describe the bug

It appears that base hrefs (e.g. <base href="/en/">) are not supported at the moment. I stumbled against two issues.

One issue is that the prerenderer/crawler grabs all hrefs and resolves the relative references against the current path. The result is something like this:

  • subsection resolved from /en/section => /section/subsection
  • section resolved from /en/section/subsection => /section/section

Note that this works fine in the browser, the problem is only with the prerenderer. I tinkered around with the code and managed to fix it by resolving against base in this line and the base itself is fairly easy to obtain in the crawl() that's called above.

The second issue is that the asset paths are relative. And the base href screws up all of them as ./_app gets resolved to /en/_app in the browser. This could be solved by allowing to turn off the asset relativity. It might be as simple as allowing / in config.kit.paths.assets. Currently it is refused by a param check with the Error: config.kit.paths.assets option must be an absolute path, if specified. and Error: config.kit.paths.assets option must not end with '/'. checks.

Reproduction

https://github.com/tontonsb/sveltekit-base-prerendering

Try npm run dev and visit http://localhost:5173/en to observe that you can either follow links or directly visit /en, /en/section and /en/section/subsection.

To observe the problem with the crawler:

  1. Execute npm run build and you'll see Not found: /section/subsection
  2. OK, let's make the language prefix optional. Try git checkout lang-optional and npm run build. Now /section/subsection will work (as the [[lang=lang]] prefix is optional in this branch), but you'll stumble against Not found: /section/section when it tries to resolve section from /section/subsection.
  3. In case you want more fun, setting config.kit.prerender.handleHttpError = 'warn' will throw you into infinite 404 /section/section/section/sect... :)

To observe the problem with assets:

  1. Do git checkout assets and npm run build && npm run preview.
  2. Browse around http://localhost:4173/en.
  3. Observe the errors in your console that report things like Not found: /en/_app/immutable/start-c96f4d66.js.

Logs

Example of the crawler error when running npm run build:

Error: Not found: /section/subsection
    at resolve (file:///path/to/sveltekit-base-prerendering/.svelte-kit/output/server/index.js:2493:18)
    at resolve (file:///path/to/sveltekit-base-prerendering/.svelte-kit/output/server/index.js:2358:34)
    at Object.#options.hooks.handle (file:///path/to/sveltekit-base-prerendering/.svelte-kit/output/server/index.js:2537:59)
    at respond (file:///path/to/sveltekit-base-prerendering/.svelte-kit/output/server/index.js:2356:43)
file:///path/to/sveltekit-base-prerendering/node_modules/@sveltejs/kit/src/core/postbuild/prerender.js:27
                                throw new Error(format(details));
                                      ^

Error: 404 /section/subsection (linked from /en)
    at file:///path/to/sveltekit-base-prerendering/node_modules/@sveltejs/kit/src/core/postbuild/prerender.js:27:11
    at save (file:///path/to/sveltekit-base-prerendering/node_modules/@sveltejs/kit/src/core/postbuild/prerender.js:346:4)
    at visit (file:///path/to/sveltekit-base-prerendering/node_modules/@sveltejs/kit/src/core/postbuild/prerender.js:195:3)
[vite-plugin-sveltekit-compile] Prerendering failed with code 1
error during build:
Error: Prerendering failed with code 1
    at ChildProcess.<anonymous> (file:///path/to/sveltekit-base-prerendering/node_modules/@sveltejs/kit/src/exports/vite/index.js:599:15)
    at ChildProcess.emit (node:events:513:28)
    at Process.ChildProcess._handle.onexit (node:internal/child_process:291:12)

Example of the asset errors after npm run build && npm run preview in branch assets:

Error: Not found: /en/_app/immutable/start-c96f4d66.js
    at resolve (file:///home/juris/sveltekit-base-prerendering/.svelte-kit/output/server/index.js:2493:18)
    at resolve (file:///home/juris/sveltekit-base-prerendering/.svelte-kit/output/server/index.js:2358:34)
    at Object.#options.hooks.handle (file:///home/juris/sveltekit-base-prerendering/.svelte-kit/output/server/index.js:2537:59)
    at respond (file:///home/juris/sveltekit-base-prerendering/.svelte-kit/output/server/index.js:2356:43)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

System Info

System:
    OS: Linux 5.10 Ubuntu 22.04.1 LTS 22.04.1 LTS (Jammy Jellyfish)
    CPU: (8) x64 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz
    Memory: 4.66 GB / 7.61 GB
    Container: Yes
    Shell: 3.6.0 - /usr/bin/fish
  Binaries:
    Node: 16.17.0 - ~/.local/share/nvm/v16.17.0/bin/node
    npm: 8.15.0 - ~/.local/share/nvm/v16.17.0/bin/npm
  npmPackages:
    @sveltejs/adapter-static: ^1.0.0 => 1.0.4 
    @sveltejs/kit: ^1.0.0 => 1.1.1 
    svelte: ^3.54.0 => 3.55.1 
    vite: ^4.0.0 => 4.0.4 

Severity

serious, but I can work around it

Additional Information

It seems that localization with a path prefix is not really possible unless one is willing to manually import a lang store and add a /{$lang}/ prefix to all internal links.

While some workarounds could be done as outlined above, the best solution would be official support for path prefixes. Be it with base href or other method of prepending the prefix to internal links.

On a related note it would be good to support something like a set of prerendering param values. E.g. I would like to render all my pages for lang in ['', 'en'] even if no internal link points to some of them. Something like the ['*'] in config.kit.prerender.entries, but with fixed param values. In the case of prefix param it might even be something like ['/*', '/en/*'] in the same config param.

tontonsb avatar Jan 16 '23 22:01 tontonsb