sveltejs / kit

web development, streamlined

Home Page:https://kit.svelte.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

When setting prerender to auto, $env/dynamic/public contains build-time environment variables

lovasoa opened this issue · comments

Describe the bug

$env/dynamic/public is supposed to contain the environment variables set at runtime.

However, when setting export const prerender = 'auto', it contains static build-time environment variable values instead.

Reproduction

src/routes/+page.js

export const prerender = 'auto';
export const ssr = false;

src/routes/+page.svelte

<script>
  import { env } from '$env/dynamic/public';
</script>

{JSON.stringify(env)}

build & run

/tmp/bug-env-app via  v16.20.2 
❯ PUBLIC_XXX='I am the build-time value' npm run build
[...] ✓ built in 1.54s

/tmp/bug-env-app via  v16.20.2 
✦ ❯ PUBLIC_XXX='I am the runtime value' PORT=8484 node build &
[1] 1272893
Listening on 0.0.0.0:8484


/tmp/bug-env-app via  v16.20.2 
✦ ❯ curl http://localhost:8484
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<link rel="icon" href="./favicon.png" />
		<meta name="viewport" content="width=device-width" />
		
		<link rel="modulepreload" href="./_app/immutable/entry/start.04cafb74.js">
		<link rel="modulepreload" href="./_app/immutable/chunks/scheduler.e108d1fd.js">
		<link rel="modulepreload" href="./_app/immutable/chunks/singletons.768d08d3.js">
		<link rel="modulepreload" href="./_app/immutable/entry/app.5f6f258b.js">
		<link rel="modulepreload" href="./_app/immutable/chunks/index.7cf2deec.js">
	</head>
	<body data-sveltekit-preload-data="hover">
		<div style="display: contents">
			<script>
				{
					__sveltekit_17vb3pv = {
						base: new URL(".", location).pathname.slice(0, -1),
						env: {"PUBLIC_XXX":"I am the build-time value"}
					};

					const element = document.currentScript.parentElement;

					Promise.all([
						import("./_app/immutable/entry/start.04cafb74.js"),
						import("./_app/immutable/entry/app.5f6f258b.js")
					]).then(([kit, app]) => {
						kit.start(app, element);
					});
				}
			</script>
		</div>
	</body>
</html>

image

Logs

No response

System Info

System:
    OS: Linux 6.2 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish)
    CPU: (8) x64 11th Gen Intel(R) Core(TM) i7-11390H @ 3.40GHz
    Memory: 2.56 GB / 15.36 GB
    Container: Yes
    Shell: 5.1.16 - /bin/bash
  Binaries:
    Node: 16.20.2 - /usr/bin/node
    Yarn: 1.22.19 - /usr/bin/yarn
    npm: 8.19.4 - /usr/bin/npm
    pnpm: 8.6.3 - /usr/bin/pnpm
  Browsers:
    Chrome: 116.0.5845.187
  npmPackages:
    @sveltejs/adapter-auto: ^2.0.0 => 2.1.0 
    @sveltejs/adapter-node: ^1.3.1 => 1.3.1 
    @sveltejs/kit: ^1.20.4 => 1.25.0 
    svelte: ^4.0.5 => 4.2.0 
    vite: ^4.4.2 => 4.4.9

Severity

serious, but I can work around it

Additional Information

No response

This is the correct behaviour. The 'auto' option is meant for dynamic pages such as /post/[id] where the value of [id] can change over time. It will prerender the pages with known values from entries or links it finds while crawling your app, but still SSR any unknown ID values (the long tail) instead of throwing a 404 immediately. If you disable SSR, it will be unable to server-side render the long tail in these cases.

In some cases you might want to prerender a route but also include it in the manifest (for example, with a route like /blog/[slug] where you want to prerender your most recent/popular content but server-render the long tail) — for these cases, there's a third option, 'auto':

https://kit.svelte.dev/docs/page-options#prerender

Admittedly, we can produce a dev time warning for this that could make it clearer.

Do you think this is a good behavior to keep ? I understand the logic from the point of view of the framework developer, but I doubt there is any case in which the application developer would want that. In my opinion, the correct behavior would be not to prerender pages that depend of runtime environment variables, instead of prerendering with incorrect (and potentially confidential) values for the variables. If the documentation says the values will be populated with runtime env variables, using build-time env variables instead is clearly a bug to me, whatever the situation.

Hello guys, I would like to contribute on this one

In my opinion, the correct behavior would be not to prerender pages that depend of runtime environment variables, instead of prerendering with incorrect (and potentially confidential) values for the variables.

Perhaps we can throw an error if a page is being prerendered with the runtime environment variables module? This would ensure expectations are aligned (that the env data will be static).

Hello guys, I would like to contribute on this one

Please feel free to submit a PR @baterson

@lovasoa @s3812497 Could you give me a hint on that please
I've been trying to understand the codebase but still couldn't figure it out what is the best place to put the bug fixing logic.

I checked postbuild/prerender thought is a good place to start, maybe in a visit call

async function visit(decoded, encoded, referrer, generated_from_id) {

But I couldn't find a convenient way to find out if module is using dynamic env variables, rather the reading it's file body.
But during visit files are compiled already so I can't check whether it was an import from $env/dynamic/

I also checked exports/vite where prerender is called and also could find a good way to solve the problem

const { prerendered, prerender_map } = await prerender({

@baterson I haven't fleshed this out but maybe a check for state.prerendering and public_env in https://github.com/sveltejs/kit/blob/master/packages/kit/src/runtime/server/page/render.js could do the trick?

public_env should be populated with runtime env variables and state.prerendering should be true if we're currently prerendering the page (I may be wrong though)

It works, thanks! This is a good place to throw the Error. I'll open a PR after fixing falling tests

I'm not sure if we should throw an error in this case. Runtime variables can also be added at built time, for example you could set a node env variable in your Vercel project dashboard which is used at build time. So while yes, in some cases it's a mistake, it's also needed in other cases - so we can't just throw an error.

@dummdidumm @s3812497 Maybe show a Warning with the same message, instead of throwing the Error?

@s3812497 I've opened a PR. Haven't found a good place to add a test though

closed as duplicate of #10008

Did a bit of digging and found this older issue. I think just providing a warning might not be the solution we want.
See #10008 (comment)