withastro / adapters

Home for Astro's core maintained adapters
47 stars 26 forks source link

can't use env in Netlify edge runtime #254

Closed jamesarosen closed 1 month ago

jamesarosen commented 2 months ago

Astro Info

Astro                    v4.7.1
Node                     v18.19.1
System                   macOS (arm64)
Package Manager          npm
Output                   server
Adapter                  @astrojs/netlify
Integrations             none

If this issue only occurs in one browser, which browser is a problem?

No response

Describe the Bug

When using Netlify's Edge Runtime, Astro compiles import.meta.env.FOO to process.env.FOO, but process.env doesn't exist for that runtime.

How I found this

astro build adds the Astro component runtime to the site's middleware stack if both (a) the middleware and (b) a .astro Page or Component import the same shared library from src/lib/, even if that library has nothing to do with Astro or HTML.

In b09bfe5a, the middleware.ts shares nothing with the *.astro files. process.env does not appear in the compiled middleware.mjs. The compiled middleware.mjs is 56710 bytes. It includes the cookie and astro/dist/core modules.

In 4551a260, the middleware.ts and Card.astro share an import, though the import is pure JS and unrelated to Astro. This import causes the compiled middleware.mjs to gain 66721 bytes, 1600 new lines, and the cssesc, kleur, html-escaper, and clsx packages. Most notably, it adds a process.env reference that breaks the Netlify build.

In 4fba3cf5, I show that just adding an import.meta.env.NODE_ENV reference to the middleware is enough to break the Netlify build.

I get this in my site's logs, though the line numbers don't match up with what's compiled here:

7:23:50 AM: > grep -C 5 -n -e "process.env" .netlify/edge-functions/middleware/middleware.mjs
7:23:50 AM: 172-    }
7:23:50 AM: 173-  }
7:23:50 AM: 174-});
7:23:50 AM: 175-
7:23:50 AM: 176-// .netlify/functions-internal/ssr/_astro-internal_middleware.mjs
7:23:50 AM: 177:var NODE_ENV = process.env.NODE_ENV;
7:23:50 AM: 178-var onRequest$1 = async (context, next) => {
7:23:50 AM: 179-  const response = await next();
7:23:50 AM: 180-  response.headers.set("middleware", "true");
7:23:50 AM: 181-  response.headers.set("NODE_ENV", NODE_ENV);
... snip
7:23:51 AM: Local version of types is up-to-date: 663522976e5ecd0008f2a7e3
7:23:51 AM: Using global installation of Deno CLI
7:23:51 AM: Using global installation of Deno CLI
7:23:51 AM: TypeError: Cannot read properties of undefined (reading 'env')
7:23:51 AM:     at file:///opt/build/repo/.netlify/edge-functions/middleware/middleware.mjs:177:24
7:23:51 AM: ​
7:23:51 AM: Bundling of edge function failed                              
7:23:51 AM: ────────────────────────────────────────────────────────────────
7:23:51 AM: ​
7:23:51 AM:   Error message
7:23:51 AM:   Could not load edge function at '/opt/build/repo/.netlify/edge-functions/middleware/middleware.mjs'. More on the Edge Functions API at https://ntl.fyi/edge-api.

What's the expected result?

The the middleware doesn't reference any Astro components or pages, the compiled middleware code should not be affected by *.astro files importing the same files that it does. The runtimes should be isolated.

According to the Netlify Edge Functions docs, Astro should probably compile those references to Netlify.env.get.

Link to Minimal Reproducible Example

https://github.com/jamesarosen/astro-broken-middleware-demo

Participation

pieh commented 2 months ago

It's unclear to me if it's best way to solve this, but this can be addressed with following:

In https://github.com/withastro/adapters/blob/f14f4d72befcb715da806fea54a8245e8f052556/packages/netlify/src/index.ts#L231-L254

have import process from 'node:process' and somewhere in global scope of written file assign that process to global space - i.e. globalThis.process = process

and finally adjust bundling config ( https://github.com/withastro/adapters/blob/f14f4d72befcb715da806fea54a8245e8f052556/packages/netlify/src/index.ts#L259-L269 ) to allow importing node:process as with current configuration it would fail (likely due to neutral platform being used). In my quick test I just added node:process as external and this resulted in successful deploy with being able to use both import.meta.env and process.env in my test middleware.

Wether this is best way to actually fix it or not is a bit TBD, but seems like what is needed is to make process available on global.

Another way (might be more idiomatic way to provide global than above) is to use inject feature of esbuild ( https://esbuild.github.io/api/#inject )