Skip to content

Commit 846540b

Browse files
authored
refactor: extract edge runtime shims into separate file (#1916)
* refactor: extract shims into separate file * chore: needs to be js
1 parent 1ec8203 commit 846540b

File tree

2 files changed

+59
-57
lines changed

2 files changed

+59
-57
lines changed

packages/runtime/src/helpers/edge.ts

+6-57
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { resolve, join } from 'path'
44
import type { NetlifyConfig, NetlifyPluginConstants } from '@netlify/build'
55
import { greenBright } from 'chalk'
66
import destr from 'destr'
7-
import { copy, copyFile, emptyDir, ensureDir, readJSON, readJson, writeJSON, writeJson } from 'fs-extra'
7+
import { copy, copyFile, emptyDir, ensureDir, readJSON, writeJSON, writeJson } from 'fs-extra'
88
import type { PrerenderManifest } from 'next/dist/build'
99
import type { MiddlewareManifest } from 'next/dist/build/webpack/plugins/middleware-plugin'
1010
import type { RouteHas } from 'next/dist/lib/load-custom-routes'
@@ -68,7 +68,7 @@ export interface FunctionManifest {
6868

6969
const maybeLoadJson = <T>(path: string): Promise<T> | null => {
7070
if (existsSync(path)) {
71-
return readJson(path)
71+
return readJSON(path)
7272
}
7373
}
7474
export const isAppDirRoute = (route: string, appPathRoutesManifest: Record<string, string> | null): boolean =>
@@ -88,60 +88,6 @@ export const loadPrerenderManifest = (netlifyConfig: NetlifyConfig): Promise<Pre
8888
*/
8989
const sanitizeName = (name: string) => `next_${name.replace(/\W/g, '_')}`
9090

91-
/**
92-
* Initialization added to the top of the edge function bundle
93-
*/
94-
const preamble = /* js */ `
95-
import {
96-
decode as _base64Decode,
97-
} from "https://deno.land/[email protected]/encoding/base64.ts";
98-
99-
import { AsyncLocalStorage } from "https://deno.land/[email protected]/node/async_hooks.ts";
100-
101-
// Deno defines "window", but naughty libraries think this means it's a browser
102-
delete globalThis.window
103-
globalThis.process = { env: {...Deno.env.toObject(), NEXT_RUNTIME: 'edge', 'NEXT_PRIVATE_MINIMAL_MODE': '1' } }
104-
globalThis.EdgeRuntime = "netlify-edge"
105-
let _ENTRIES = {}
106-
107-
// Next.js expects this as a global
108-
globalThis.AsyncLocalStorage = AsyncLocalStorage
109-
110-
// Next.js uses this extension to the Headers API implemented by Cloudflare workerd
111-
if(!('getAll' in Headers.prototype)) {
112-
Headers.prototype.getAll = function getAll(name) {
113-
name = name.toLowerCase();
114-
if (name !== "set-cookie") {
115-
throw new Error("Headers.getAll is only supported for Set-Cookie");
116-
}
117-
return [...this.entries()]
118-
.filter(([key]) => key === name)
119-
.map(([, value]) => value);
120-
};
121-
}
122-
// Next uses blob: urls to refer to local assets, so we need to intercept these
123-
const _fetch = globalThis.fetch
124-
const fetch = async (url, init) => {
125-
try {
126-
if (typeof url === 'object' && url.href?.startsWith('blob:')) {
127-
const key = url.href.slice(5)
128-
if (key in _ASSETS) {
129-
return new Response(_base64Decode(_ASSETS[key]))
130-
}
131-
}
132-
return await _fetch(url, init)
133-
} catch (error) {
134-
console.error(error)
135-
throw error
136-
}
137-
}
138-
139-
// Next edge runtime uses "self" as a function-scoped global-like object, but some of the older polyfills expect it to equal globalThis
140-
// See https://nextjs.org/docs/basic-features/supported-browsers-features#polyfills
141-
const self = { ...globalThis, fetch }
142-
143-
`
144-
14591
// Slightly different spacing in different versions!
14692
const IMPORT_UNSUPPORTED = [
14793
`Object.defineProperty(globalThis,"__import_unsupported"`,
@@ -158,7 +104,10 @@ const getMiddlewareBundle = async ({
158104
netlifyConfig: NetlifyConfig
159105
}): Promise<string> => {
160106
const { publish } = netlifyConfig.build
161-
const chunks: Array<string> = [preamble]
107+
108+
const shims = await fs.readFile(getEdgeTemplatePath('shims.js'), 'utf8')
109+
110+
const chunks: Array<string> = [shims]
162111

163112
chunks.push(`export const _DEFINITION = ${JSON.stringify(edgeFunctionDefinition)}`)
164113

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// @ts-check
2+
// deno-lint-ignore-file no-var prefer-const no-unused-vars no-explicit-any
3+
import { decode as _base64Decode } from 'https://deno.land/[email protected]/encoding/base64.ts'
4+
import { AsyncLocalStorage as ALSCompat } from 'https://deno.land/[email protected]/node/async_hooks.ts'
5+
6+
/**
7+
* These are the shims, polyfills and other kludges to make Next.js work in standards-compliant runtime.
8+
* This file isn't imported, but is instead inlined along with other chunks into the edge bundle.
9+
*/
10+
11+
// Deno defines "window", but naughty libraries think this means it's a browser
12+
// @ts-ignore
13+
delete globalThis.window
14+
globalThis.process = {
15+
env: { ...Deno.env.toObject(), NEXT_RUNTIME: 'edge', NEXT_PRIVATE_MINIMAL_MODE: '1' },
16+
}
17+
globalThis.EdgeRuntime = 'netlify-edge'
18+
let _ENTRIES = {}
19+
20+
// Next.js expects this as a global
21+
globalThis.AsyncLocalStorage = ALSCompat
22+
23+
// Next.js uses this extension to the Headers API implemented by Cloudflare workerd
24+
if (!('getAll' in Headers.prototype)) {
25+
// @ts-ignore
26+
Headers.prototype.getAll = function getAll(name) {
27+
name = name.toLowerCase()
28+
if (name !== 'set-cookie') {
29+
throw new Error('Headers.getAll is only supported for Set-Cookie')
30+
}
31+
return [...this.entries()].filter(([key]) => key === name).map(([, value]) => value)
32+
}
33+
}
34+
// Next uses blob: urls to refer to local assets, so we need to intercept these
35+
const _fetch = globalThis.fetch
36+
const fetch /* type {typeof globalThis.fetch} */ = async (url, init) => {
37+
try {
38+
if (url instanceof URL && url.href?.startsWith('blob:')) {
39+
const key = url.href.slice(5)
40+
if (key in _ASSETS) {
41+
return new Response(_base64Decode(_ASSETS[key]))
42+
}
43+
}
44+
return await _fetch(url, init)
45+
} catch (error) {
46+
console.error(error)
47+
throw error
48+
}
49+
}
50+
51+
// Next edge runtime uses "self" as a function-scoped global-like object, but some of the older polyfills expect it to equal globalThis
52+
// See https://nextjs.org/docs/basic-features/supported-browsers-features#polyfills
53+
const self = { ...globalThis, fetch }

0 commit comments

Comments
 (0)