diff --git a/.changeset/healthy-beans-heal.md b/.changeset/healthy-beans-heal.md new file mode 100644 index 000000000..397e651ee --- /dev/null +++ b/.changeset/healthy-beans-heal.md @@ -0,0 +1,5 @@ +--- +'@cloudflare/next-on-pages': patch +--- + +fix Next.js re-defining global `__import_unsupported` diff --git a/.changeset/rich-dogs-push.md b/.changeset/rich-dogs-push.md new file mode 100644 index 000000000..892575ec4 --- /dev/null +++ b/.changeset/rich-dogs-push.md @@ -0,0 +1,5 @@ +--- +'@cloudflare/next-on-pages': patch +--- + +fix `AbortController`s being created in the global scope diff --git a/packages/next-on-pages/templates/_worker.js/index.ts b/packages/next-on-pages/templates/_worker.js/index.ts index 50cf212af..66e3225b1 100644 --- a/packages/next-on-pages/templates/_worker.js/index.ts +++ b/packages/next-on-pages/templates/_worker.js/index.ts @@ -21,6 +21,66 @@ declare const __ALSes_PROMISE__: Promise; }>; +const originalDefineProperty = Object.defineProperty; + +const patchedDefineProperty = ( + ...args: Parameters> +) => { + const target = args[0]; + const key = args[1]; + // Next.js defined an __import_unsupported global property as non configurable + // with next-on-pages this apps try to re-define this property multiple times, + // so here we patch `defineProperty` to just ignore re-definition of such property + const importUnsupportedKey = '__import_unsupported'; + if (key === importUnsupportedKey) { + if ( + typeof target === 'object' && + target !== null && + importUnsupportedKey in target + ) { + return; + } + } + return originalDefineProperty(...args); +}; + +global.Object.defineProperty = + patchedDefineProperty as typeof global.Object.defineProperty; + +global.AbortController = class PatchedAbortController extends AbortController { + constructor() { + try { + super(); + } catch (e) { + if ( + e instanceof Error && + e.message.includes('Disallowed operation called within global scope') + ) { + // Next.js attempted to create an AbortController in the global scope + // let's return something that looks like an AbortController but with + // noop functionalities + return { + signal: { + aborted: false, + reason: null, + onabort: () => { + /* empty */ + }, + throwIfAborted: () => { + /* empty */ + }, + } as unknown as AbortSignal, + abort() { + /* empty */ + }, + }; + } else { + throw e; + } + } + } +}; + export default { async fetch(request, env, ctx) { setupRoutesIsolation();