From 168ea3d3a7901e45a5e41ca52dbbe1d4d72f0e7a Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Mon, 15 Apr 2024 21:50:38 +0200 Subject: [PATCH 01/14] build(edge): extract buildId into environment --- packages/next/src/build/index.ts | 3 +- packages/next/src/build/webpack-config.ts | 5 +- .../webpack/plugins/build-manifest-plugin.ts | 53 ++++++++++++++++--- .../e2e/middleware-general/test/index.test.ts | 1 + 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index f1b5a6c6805d9..14b3fe444ec3f 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -2768,7 +2768,8 @@ export default async function build( }, ] - routes.forEach((route) => { + // Always sort the routes to get consistent output in manifests + getSortedRoutes(routes).forEach((route) => { if (isDynamicRoute(page) && route === page) return if (route === UNDERSCORE_NOT_FOUND_ROUTE) return diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts index 4541fb108b73e..2e3bdb8b6757a 100644 --- a/packages/next/src/build/webpack-config.ts +++ b/packages/next/src/build/webpack-config.ts @@ -1821,7 +1821,10 @@ export default async function getBaseWebpackConfig( dev, sriEnabled: !dev && !!config.experimental.sri?.algorithm, rewrites, - edgeEnvironments: edgePreviewProps || {}, + edgeEnvironments: { + ...edgePreviewProps, + buildId, + }, }), isClient && new BuildManifestPlugin({ diff --git a/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts b/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts index bc2b5621d3e58..3b1b9c0e97966 100644 --- a/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts +++ b/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts @@ -29,6 +29,34 @@ export type ClientBuildManifest = { // generated). export const srcEmptySsgManifest = `self.__SSG_MANIFEST=new Set;self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()` +// Return different path for edge runtime and nodejs runtime +// edge: '"/static/" + process.env.NEXT_BUILD_ID + "/low-priority.js"' +// nodejs: '/static//low-priority.js' +function buildLowPriorityPath( + filename: string, + buildId: string, + isEdgeRuntime: boolean +) { + return isEdgeRuntime + ? `"${CLIENT_STATIC_FILES_PATH}/" + process.env.NEXT_BUILD_ID + "/${filename}"` + : `${CLIENT_STATIC_FILES_PATH}/${buildId}/${filename}` +} + +function createEdgeRuntimeManifest( + originAssetMap: BuildManifest, + buildId: string +): string { + const assetMap = { + ...originAssetMap, + lowPriorityFiles: [ + buildLowPriorityPath('_buildManifest.js', buildId, true), + buildLowPriorityPath('_ssgManifest.js', buildId, true), + ], + } + + return JSON.stringify(assetMap, null, 2) +} + function normalizeRewrite(item: { source: string destination: string @@ -231,19 +259,27 @@ export default class BuildManifestPlugin { // Add the runtime build manifest file (generated later in this file) // as a dependency for the app. If the flag is false, the file won't be // downloaded by the client. - assetMap.lowPriorityFiles.push( - `${CLIENT_STATIC_FILES_PATH}/${this.buildId}/_buildManifest.js` + const buildManifestPath = buildLowPriorityPath( + '_buildManifest.js', + this.buildId, + false ) - const ssgManifestPath = `${CLIENT_STATIC_FILES_PATH}/${this.buildId}/_ssgManifest.js` - - assetMap.lowPriorityFiles.push(ssgManifestPath) + const ssgManifestPath = buildLowPriorityPath( + '_ssgManifest.js', + this.buildId, + false + ) + assetMap.lowPriorityFiles.push(buildManifestPath, ssgManifestPath) assets[ssgManifestPath] = new sources.RawSource(srcEmptySsgManifest) } assetMap.pages = Object.keys(assetMap.pages) .sort() // eslint-disable-next-line - .reduce((a, c) => ((a[c] = assetMap.pages[c]), a), {} as any) + .reduce( + (a, c) => ((a[c] = assetMap.pages[c]), a), + {} as typeof assetMap.pages + ) let buildManifestName = BUILD_MANIFEST @@ -256,7 +292,10 @@ export default class BuildManifestPlugin { ) assets[`server/${MIDDLEWARE_BUILD_MANIFEST}.js`] = new sources.RawSource( - `self.__BUILD_MANIFEST=${JSON.stringify(assetMap)}` + `self.__BUILD_MANIFEST=${createEdgeRuntimeManifest( + assetMap, + this.buildId + )}` ) if (!this.isDevFallback) { diff --git a/test/e2e/middleware-general/test/index.test.ts b/test/e2e/middleware-general/test/index.test.ts index e5c23b4fe51ca..95831f2cd67ca 100644 --- a/test/e2e/middleware-general/test/index.test.ts +++ b/test/e2e/middleware-general/test/index.test.ts @@ -186,6 +186,7 @@ describe('Middleware Runtime', () => { regions: 'auto', }) expect(envs).toContainAllKeys([ + 'buildId', 'previewModeEncryptionKey', 'previewModeId', 'previewModeSigningKey', From 63ac460367f3f4b2857564b916bd4c3c17a3bd5e Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Mon, 29 Apr 2024 15:21:31 +0200 Subject: [PATCH 02/14] exteranlize sa encryption key --- .../next/src/build/templates/edge-ssr-app.ts | 2 +- packages/next/src/build/templates/edge-ssr.ts | 2 +- packages/next/src/build/webpack-config.ts | 3 +- .../webpack/plugins/build-manifest-plugin.ts | 4 +-- .../plugins/flight-client-entry-plugin.ts | 29 ++++++++++++------- .../webpack/plugins/middleware-plugin.ts | 12 ++++++-- 6 files changed, 35 insertions(+), 17 deletions(-) diff --git a/packages/next/src/build/templates/edge-ssr-app.ts b/packages/next/src/build/templates/edge-ssr-app.ts index 4d0de74d437f3..2202528b15d55 100644 --- a/packages/next/src/build/templates/edge-ssr-app.ts +++ b/packages/next/src/build/templates/edge-ssr-app.ts @@ -78,7 +78,7 @@ const render = getRender({ serverActions: isServerComponent ? serverActions : undefined, subresourceIntegrityManifest, config: nextConfig, - buildId: 'VAR_BUILD_ID', + buildId: process.env.__NEXT_BUILD_ID || 'VAR_BUILD_ID', nextFontManifest, incrementalCacheHandler, interceptionRouteRewrites, diff --git a/packages/next/src/build/templates/edge-ssr.ts b/packages/next/src/build/templates/edge-ssr.ts index d5611928ab102..620111d912105 100644 --- a/packages/next/src/build/templates/edge-ssr.ts +++ b/packages/next/src/build/templates/edge-ssr.ts @@ -104,7 +104,7 @@ const render = getRender({ reactLoadableManifest, subresourceIntegrityManifest, config: nextConfig, - buildId: 'VAR_BUILD_ID', + buildId: process.env.__NEXT_BUILD_ID || 'VAR_BUILD_ID', nextFontManifest, incrementalCacheHandler, }) diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts index 2e3bdb8b6757a..594d5972eec5a 100644 --- a/packages/next/src/build/webpack-config.ts +++ b/packages/next/src/build/webpack-config.ts @@ -1822,8 +1822,9 @@ export default async function getBaseWebpackConfig( sriEnabled: !dev && !!config.experimental.sri?.algorithm, rewrites, edgeEnvironments: { + __NEXT_BUILD_ID: buildId, + NEXT_SERVER_ACTIONS_ENCRYPTION_KEY: encryptionKey, ...edgePreviewProps, - buildId, }, }), isClient && diff --git a/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts b/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts index 3b1b9c0e97966..dc0b4945f638f 100644 --- a/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts +++ b/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts @@ -30,7 +30,7 @@ export type ClientBuildManifest = { export const srcEmptySsgManifest = `self.__SSG_MANIFEST=new Set;self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()` // Return different path for edge runtime and nodejs runtime -// edge: '"/static/" + process.env.NEXT_BUILD_ID + "/low-priority.js"' +// edge: '"/static/" + process.env.__NEXT_BUILD_ID + "/low-priority.js"' // nodejs: '/static//low-priority.js' function buildLowPriorityPath( filename: string, @@ -38,7 +38,7 @@ function buildLowPriorityPath( isEdgeRuntime: boolean ) { return isEdgeRuntime - ? `"${CLIENT_STATIC_FILES_PATH}/" + process.env.NEXT_BUILD_ID + "/${filename}"` + ? `"${CLIENT_STATIC_FILES_PATH}/" + process.env.__NEXT_BUILD_ID + "/${filename}"` : `${CLIENT_STATIC_FILES_PATH}/${buildId}/${filename}` } diff --git a/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts b/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts index 932c5356c3e04..fddddfe097c6e 100644 --- a/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts @@ -1016,20 +1016,29 @@ export class FlightClientEntryPlugin { edgeServerActions[id] = action } - const json = JSON.stringify( - { - node: serverActions, - edge: edgeServerActions, - encryptionKey: this.encryptionKey, - }, + const serverManifest = { + node: serverActions, + edge: edgeServerActions, + encryptionKey: this.encryptionKey, + } + const edgeServerManifest = { + ...serverManifest, + encryptionKey: 'process.env.__NEXT_SERVER_ACTION_ENCRYPTION_KEY', + } + + const json = JSON.stringify(serverManifest, null, this.dev ? 2 : undefined) + const edgeJson = JSON.stringify( + edgeServerManifest, null, this.dev ? 2 : undefined ) - assets[`${this.assetPrefix}${SERVER_REFERENCE_MANIFEST}.js`] = - new sources.RawSource( - `self.__RSC_SERVER_MANIFEST=${JSON.stringify(json)}` - ) as unknown as webpack.sources.RawSource + if (this.isEdgeServer) { + assets[`${this.assetPrefix}${SERVER_REFERENCE_MANIFEST}.js`] = + new sources.RawSource( + `self.__RSC_SERVER_MANIFEST=${JSON.stringify(edgeJson)}` + ) as unknown as webpack.sources.RawSource + } assets[`${this.assetPrefix}${SERVER_REFERENCE_MANIFEST}.json`] = new sources.RawSource(json) as unknown as webpack.sources.RawSource } diff --git a/packages/next/src/build/webpack/plugins/middleware-plugin.ts b/packages/next/src/build/webpack/plugins/middleware-plugin.ts index 5c9ec94fd14c2..f01f359b0fe09 100644 --- a/packages/next/src/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/src/build/webpack/plugins/middleware-plugin.ts @@ -739,18 +739,26 @@ function getExtractMetadata(params: { } } +// These values will be replaced again in edge runtime deployment build. +// `buildId` represents BUILD_ID to be externalized in env vars. +// `encryptionKey` represents server action encryption key to be externalized in env vars. +type EdgeRuntimeEnvironments = Record & { + __NEXT_BUILD_ID: string + NEXT_SERVER_ACTIONS_ENCRYPTION_KEY: string +} + interface Options { dev: boolean sriEnabled: boolean rewrites: CustomRoutes['rewrites'] - edgeEnvironments: Record + edgeEnvironments: EdgeRuntimeEnvironments } export default class MiddlewarePlugin { private readonly dev: Options['dev'] private readonly sriEnabled: Options['sriEnabled'] private readonly rewrites: Options['rewrites'] - private readonly edgeEnvironments: Record + private readonly edgeEnvironments: EdgeRuntimeEnvironments constructor({ dev, sriEnabled, rewrites, edgeEnvironments }: Options) { this.dev = dev From 497b6361505d181a618bef193845e4b46e06cabf Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Mon, 29 Apr 2024 20:43:33 +0200 Subject: [PATCH 03/14] read envs --- packages/next/src/build/entries.ts | 2 +- .../next/src/build/templates/edge-ssr-app.ts | 2 +- packages/next/src/build/templates/edge-ssr.ts | 2 +- .../loaders/next-edge-ssr-loader/index.ts | 4 +-- .../webpack/plugins/middleware-plugin.ts | 2 +- packages/next/src/server/next-server.ts | 2 ++ .../next/src/server/web/sandbox/context.ts | 30 ++++++++++++------- 7 files changed, 28 insertions(+), 16 deletions(-) diff --git a/packages/next/src/build/entries.ts b/packages/next/src/build/entries.ts index 7ae4c637f1b98..12986e9c27dcd 100644 --- a/packages/next/src/build/entries.ts +++ b/packages/next/src/build/entries.ts @@ -406,7 +406,7 @@ export function getEdgeServerEntry(opts: { absoluteDocumentPath: opts.pages['/_document'], absoluteErrorPath: opts.pages['/_error'], absolutePagePath: opts.absolutePagePath, - buildId: opts.buildId, + buildId: 'process.env.__NEXT_BUILD_ID', // opts.buildId, dev: opts.isDev, isServerComponent: opts.isServerComponent, page: opts.page, diff --git a/packages/next/src/build/templates/edge-ssr-app.ts b/packages/next/src/build/templates/edge-ssr-app.ts index 2202528b15d55..195f9d1726de1 100644 --- a/packages/next/src/build/templates/edge-ssr-app.ts +++ b/packages/next/src/build/templates/edge-ssr-app.ts @@ -78,7 +78,7 @@ const render = getRender({ serverActions: isServerComponent ? serverActions : undefined, subresourceIntegrityManifest, config: nextConfig, - buildId: process.env.__NEXT_BUILD_ID || 'VAR_BUILD_ID', + buildId: process.env.__NEXT_BUILD_ID!, nextFontManifest, incrementalCacheHandler, interceptionRouteRewrites, diff --git a/packages/next/src/build/templates/edge-ssr.ts b/packages/next/src/build/templates/edge-ssr.ts index 620111d912105..6a311abe7b145 100644 --- a/packages/next/src/build/templates/edge-ssr.ts +++ b/packages/next/src/build/templates/edge-ssr.ts @@ -104,7 +104,7 @@ const render = getRender({ reactLoadableManifest, subresourceIntegrityManifest, config: nextConfig, - buildId: process.env.__NEXT_BUILD_ID || 'VAR_BUILD_ID', + buildId: process.env.__NEXT_BUILD_ID!, nextFontManifest, incrementalCacheHandler, }) diff --git a/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts b/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts index 8e82cf9aabe23..0a4ce70d6aac5 100644 --- a/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts +++ b/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts @@ -145,7 +145,7 @@ const edgeSSRLoader: webpack.LoaderDefinitionFunction = { VAR_USERLAND: pageModPath, VAR_PAGE: page, - VAR_BUILD_ID: buildId, + // VAR_BUILD_ID: buildId, }, { sriEnabled: JSON.stringify(sriEnabled), @@ -167,7 +167,7 @@ const edgeSSRLoader: webpack.LoaderDefinitionFunction = { VAR_USERLAND: pageModPath, VAR_PAGE: page, - VAR_BUILD_ID: buildId, + // VAR_BUILD_ID: buildId, VAR_MODULE_DOCUMENT: documentPath, VAR_MODULE_APP: appPath, VAR_MODULE_GLOBAL_ERROR: errorPath, diff --git a/packages/next/src/build/webpack/plugins/middleware-plugin.ts b/packages/next/src/build/webpack/plugins/middleware-plugin.ts index f01f359b0fe09..213988cbc99cb 100644 --- a/packages/next/src/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/src/build/webpack/plugins/middleware-plugin.ts @@ -42,10 +42,10 @@ export interface EdgeFunctionDefinition { name: string page: string matchers: MiddlewareMatcher[] + environments: Record wasm?: AssetBinding[] assets?: AssetBinding[] regions?: string[] | string - environments?: Record } export interface MiddlewareManifest { diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index 8f6f3e5f8cd60..bd9bd32657d91 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -1435,6 +1435,7 @@ export default class NextNodeServer extends BaseServer< name: string paths: string[] wasm: { filePath: string; name: string }[] + environments: { [key: string]: string } assets?: { filePath: string; name: string }[] } | null { const manifest = this.getMiddlewareManifest() @@ -1476,6 +1477,7 @@ export default class NextNodeServer extends BaseServer< filePath: join(this.distDir, binding.filePath), } }), + environments: pageInfo.environments, } } diff --git a/packages/next/src/server/web/sandbox/context.ts b/packages/next/src/server/web/sandbox/context.ts index a872f4dd31c51..4d4901e9fdbe7 100644 --- a/packages/next/src/server/web/sandbox/context.ts +++ b/packages/next/src/server/web/sandbox/context.ts @@ -107,9 +107,14 @@ async function loadWasm( return modules } -function buildEnvironmentVariablesFrom(): Record { +function buildEnvironmentVariablesFrom( + injectedEnvironments: Record +): Record { const pairs = Object.keys(process.env).map((key) => [key, process.env[key]]) const env = Object.fromEntries(pairs) + for (const key of Object.keys(injectedEnvironments)) { + env[key] = injectedEnvironments[key] + } env.NEXT_RUNTIME = 'edge' return env } @@ -122,15 +127,16 @@ Learn more: https://nextjs.org/docs/api-reference/edge-runtime`) throw error } -function createProcessPolyfill() { - const processPolyfill = { env: buildEnvironmentVariablesFrom() } - const overridenValue: Record = {} +function createProcessPolyfill(environments: Record) { + const processPolyfill = { env: buildEnvironmentVariablesFrom(environments) } + const overriddenValue: Record = {} + for (const key of Object.keys(process)) { if (key === 'env') continue Object.defineProperty(processPolyfill, key, { get() { - if (overridenValue[key] !== undefined) { - return overridenValue[key] + if (overriddenValue[key] !== undefined) { + return overriddenValue[key] } if (typeof (process as any)[key] === 'function') { return () => throwUnsupportedAPIError(`process.${key}`) @@ -138,7 +144,7 @@ function createProcessPolyfill() { return undefined }, set(value) { - overridenValue[key] = value + overriddenValue[key] = value }, enumerable: false, }) @@ -244,14 +250,15 @@ export const requestStore = new AsyncLocalStorage<{ async function createModuleContext(options: ModuleContextOptions) { const warnedEvals = new Set() const warnedWasmCodegens = new Set() - const wasm = await loadWasm(options.edgeFunctionEntry.wasm ?? []) + const { edgeFunctionEntry } = options + const wasm = await loadWasm(edgeFunctionEntry.wasm ?? []) const runtime = new EdgeRuntime({ codeGeneration: process.env.NODE_ENV !== 'production' ? { strings: true, wasm: true } : undefined, extend: (context) => { - context.process = createProcessPolyfill() + context.process = createProcessPolyfill(edgeFunctionEntry.environments) Object.defineProperty(context, 'require', { enumerable: false, @@ -470,7 +477,10 @@ interface ModuleContextOptions { onWarning: (warn: Error) => void useCache: boolean distDir: string - edgeFunctionEntry: Pick + edgeFunctionEntry: Pick< + EdgeFunctionDefinition, + 'assets' | 'wasm' | 'environments' + > } function getModuleContextShared(options: ModuleContextOptions) { From 2926f72548e818645cbe3b24b0fa6c7f2f4466a9 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Tue, 30 Apr 2024 14:13:03 +0200 Subject: [PATCH 04/14] fix lint --- .../webpack/loaders/next-edge-ssr-loader/index.ts | 1 - .../src/build/webpack/plugins/build-manifest-plugin.ts | 2 +- .../webpack/plugins/flight-client-entry-plugin.ts | 10 ++++------ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts b/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts index 0a4ce70d6aac5..f45f5e86b4ef8 100644 --- a/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts +++ b/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts @@ -65,7 +65,6 @@ const edgeSSRLoader: webpack.LoaderDefinitionFunction = const { dev, page, - buildId, absolutePagePath, absoluteAppPath, absoluteDocumentPath, diff --git a/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts b/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts index dc0b4945f638f..18c45d3971026 100644 --- a/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts +++ b/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts @@ -275,8 +275,8 @@ export default class BuildManifestPlugin { assetMap.pages = Object.keys(assetMap.pages) .sort() - // eslint-disable-next-line .reduce( + // eslint-disable-next-line (a, c) => ((a[c] = assetMap.pages[c]), a), {} as typeof assetMap.pages ) diff --git a/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts b/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts index fddddfe097c6e..aa33e3c7614a7 100644 --- a/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts @@ -1033,12 +1033,10 @@ export class FlightClientEntryPlugin { this.dev ? 2 : undefined ) - if (this.isEdgeServer) { - assets[`${this.assetPrefix}${SERVER_REFERENCE_MANIFEST}.js`] = - new sources.RawSource( - `self.__RSC_SERVER_MANIFEST=${JSON.stringify(edgeJson)}` - ) as unknown as webpack.sources.RawSource - } + assets[`${this.assetPrefix}${SERVER_REFERENCE_MANIFEST}.js`] = + new sources.RawSource( + `self.__RSC_SERVER_MANIFEST=${JSON.stringify(edgeJson)}` + ) as unknown as webpack.sources.RawSource assets[`${this.assetPrefix}${SERVER_REFERENCE_MANIFEST}.json`] = new sources.RawSource(json) as unknown as webpack.sources.RawSource } From c3903bd24ce349b1229f622f80d05a38031d2fda Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Tue, 30 Apr 2024 14:25:29 +0200 Subject: [PATCH 05/14] fix env key --- .../src/build/webpack/plugins/flight-client-entry-plugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts b/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts index aa33e3c7614a7..101171976b011 100644 --- a/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts @@ -1023,7 +1023,7 @@ export class FlightClientEntryPlugin { } const edgeServerManifest = { ...serverManifest, - encryptionKey: 'process.env.__NEXT_SERVER_ACTION_ENCRYPTION_KEY', + encryptionKey: 'process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY', } const json = JSON.stringify(serverManifest, null, this.dev ? 2 : undefined) From a240a7060aaf14b3387b90be38c06b783e64652e Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Tue, 30 Apr 2024 20:33:31 +0200 Subject: [PATCH 06/14] rename and build id --- packages/next/src/build/entries.ts | 1 - .../src/build/webpack/plugins/middleware-plugin.ts | 4 ++-- packages/next/src/server/next-server.ts | 4 ++-- packages/next/src/server/web/sandbox/context.ts | 11 ++++------- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/next/src/build/entries.ts b/packages/next/src/build/entries.ts index 12986e9c27dcd..9652b0a356d49 100644 --- a/packages/next/src/build/entries.ts +++ b/packages/next/src/build/entries.ts @@ -406,7 +406,6 @@ export function getEdgeServerEntry(opts: { absoluteDocumentPath: opts.pages['/_document'], absoluteErrorPath: opts.pages['/_error'], absolutePagePath: opts.absolutePagePath, - buildId: 'process.env.__NEXT_BUILD_ID', // opts.buildId, dev: opts.isDev, isServerComponent: opts.isServerComponent, page: opts.page, diff --git a/packages/next/src/build/webpack/plugins/middleware-plugin.ts b/packages/next/src/build/webpack/plugins/middleware-plugin.ts index 213988cbc99cb..866aff2ae1a32 100644 --- a/packages/next/src/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/src/build/webpack/plugins/middleware-plugin.ts @@ -42,7 +42,7 @@ export interface EdgeFunctionDefinition { name: string page: string matchers: MiddlewareMatcher[] - environments: Record + env: Record wasm?: AssetBinding[] assets?: AssetBinding[] regions?: string[] | string @@ -227,7 +227,7 @@ function getCreateAssets(params: { name, filePath, })), - environments: opts.edgeEnvironments, + env: opts.edgeEnvironments, ...(metadata.regions && { regions: metadata.regions }), } diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index bd9bd32657d91..1b2efb33b090a 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -1435,7 +1435,7 @@ export default class NextNodeServer extends BaseServer< name: string paths: string[] wasm: { filePath: string; name: string }[] - environments: { [key: string]: string } + env: { [key: string]: string } assets?: { filePath: string; name: string }[] } | null { const manifest = this.getMiddlewareManifest() @@ -1477,7 +1477,7 @@ export default class NextNodeServer extends BaseServer< filePath: join(this.distDir, binding.filePath), } }), - environments: pageInfo.environments, + env: pageInfo.env, } } diff --git a/packages/next/src/server/web/sandbox/context.ts b/packages/next/src/server/web/sandbox/context.ts index 4d4901e9fdbe7..899758cb46f2a 100644 --- a/packages/next/src/server/web/sandbox/context.ts +++ b/packages/next/src/server/web/sandbox/context.ts @@ -127,8 +127,8 @@ Learn more: https://nextjs.org/docs/api-reference/edge-runtime`) throw error } -function createProcessPolyfill(environments: Record) { - const processPolyfill = { env: buildEnvironmentVariablesFrom(environments) } +function createProcessPolyfill(env: Record) { + const processPolyfill = { env: buildEnvironmentVariablesFrom(env) } const overriddenValue: Record = {} for (const key of Object.keys(process)) { @@ -258,7 +258,7 @@ async function createModuleContext(options: ModuleContextOptions) { ? { strings: true, wasm: true } : undefined, extend: (context) => { - context.process = createProcessPolyfill(edgeFunctionEntry.environments) + context.process = createProcessPolyfill(edgeFunctionEntry.env) Object.defineProperty(context, 'require', { enumerable: false, @@ -477,10 +477,7 @@ interface ModuleContextOptions { onWarning: (warn: Error) => void useCache: boolean distDir: string - edgeFunctionEntry: Pick< - EdgeFunctionDefinition, - 'assets' | 'wasm' | 'environments' - > + edgeFunctionEntry: Pick } function getModuleContextShared(options: ModuleContextOptions) { From 84a995d1d5cdc9f7b6e972c8df212c1682115cc0 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Tue, 30 Apr 2024 20:35:50 +0200 Subject: [PATCH 07/14] fix build --- .../next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts b/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts index f45f5e86b4ef8..acbceaf96fa63 100644 --- a/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts +++ b/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts @@ -16,7 +16,6 @@ export type EdgeSSRLoaderQuery = { absoluteDocumentPath: string absoluteErrorPath: string absolutePagePath: string - buildId: string dev: boolean isServerComponent: boolean page: string From c43da90550f68f4c8dcb51591ad9812ba67e0a87 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Tue, 30 Apr 2024 21:50:42 +0200 Subject: [PATCH 08/14] make turbopack changes and fixes --- .../crates/napi/src/next_api/project.rs | 45 +++++- packages/next-swc/crates/next-api/src/app.rs | 1 + .../crates/next-api/src/middleware.rs | 1 + .../next-swc/crates/next-api/src/pages.rs | 1 + .../next-swc/crates/next-api/src/project.rs | 135 ++++++++++++++---- .../next-core/src/next_app/app_page_entry.rs | 2 - .../next-core/src/next_manifests/mod.rs | 3 +- .../next-core/src/next_pages/page_entry.rs | 2 - packages/next/src/build/index.ts | 3 + packages/next/src/build/swc/index.ts | 17 ++- packages/next/src/build/webpack-build/impl.ts | 9 +- .../loaders/next-edge-ssr-loader/index.ts | 2 - .../src/server/dev/hot-reloader-turbopack.ts | 6 +- test/development/basic/next-rs-api.test.ts | 7 + 14 files changed, 193 insertions(+), 41 deletions(-) diff --git a/packages/next-swc/crates/napi/src/next_api/project.rs b/packages/next-swc/crates/napi/src/next_api/project.rs index 396f6ad2b6c58..1c70a93c7a5f9 100644 --- a/packages/next-swc/crates/napi/src/next_api/project.rs +++ b/packages/next-swc/crates/napi/src/next_api/project.rs @@ -9,8 +9,8 @@ use napi::{ use next_api::{ entrypoints::Entrypoints, project::{ - DefineEnv, Instrumentation, Middleware, PartialProjectOptions, Project, ProjectContainer, - ProjectOptions, + DefineEnv, DraftModeOptions, Instrumentation, Middleware, PartialProjectOptions, Project, + ProjectContainer, ProjectOptions, }, route::{Endpoint, Route}, }; @@ -63,6 +63,23 @@ pub struct NapiEnvVar { pub value: String, } +#[napi(object)] +pub struct NapiDraftModeOptions { + pub preview_mode_id: String, + pub preview_mode_encryption_key: String, + pub preview_mode_signing_key: String, +} + +impl From for DraftModeOptions { + fn from(val: NapiDraftModeOptions) -> Self { + DraftModeOptions { + preview_mode_id: val.preview_mode_id, + preview_mode_encryption_key: val.preview_mode_encryption_key, + preview_mode_signing_key: val.preview_mode_signing_key, + } + } +} + #[napi(object)] pub struct NapiProjectOptions { /// A root path from which all files must be nested under. Trying to access @@ -94,6 +111,15 @@ pub struct NapiProjectOptions { /// The mode in which Next.js is running. pub dev: bool, + + /// The server actions encryption key. + pub encryption_key: String, + + /// The build id. + pub build_id: String, + + /// Options for draft mode. + pub preview_props: NapiDraftModeOptions, } /// [NapiProjectOptions] with all fields optional. @@ -128,6 +154,15 @@ pub struct NapiPartialProjectOptions { /// The mode in which Next.js is running. pub dev: Option, + + /// The server actions encryption key. + pub encryption_key: Option, + + /// The build id. + pub build_id: Option, + + /// Options for draft mode. + pub preview_props: Option, } #[napi(object)] @@ -159,6 +194,9 @@ impl From for ProjectOptions { .collect(), define_env: val.define_env.into(), dev: val.dev, + encryption_key: val.encryption_key, + build_id: val.build_id, + preview_props: val.preview_props.into(), } } } @@ -176,6 +214,9 @@ impl From for PartialProjectOptions { .map(|env| env.into_iter().map(|var| (var.name, var.value)).collect()), define_env: val.define_env.map(|env| env.into()), dev: val.dev, + encryption_key: val.encryption_key, + build_id: val.build_id, + preview_props: val.preview_props.map(|props| props.into()), } } } diff --git a/packages/next-swc/crates/next-api/src/app.rs b/packages/next-swc/crates/next-api/src/app.rs index 21763411e2ef8..61515c5c4f35c 100644 --- a/packages/next-swc/crates/next-api/src/app.rs +++ b/packages/next-swc/crates/next-api/src/app.rs @@ -1100,6 +1100,7 @@ impl AppEndpoint { .clone() .map(Regions::Multiple), matchers: vec![matchers], + env: this.app_project.project().edge_env().await?.clone_value(), ..Default::default() }; let middleware_manifest_v2 = MiddlewaresManifestV2 { diff --git a/packages/next-swc/crates/next-api/src/middleware.rs b/packages/next-swc/crates/next-api/src/middleware.rs index 89fbd46c50a86..f509b954155f3 100644 --- a/packages/next-swc/crates/next-api/src/middleware.rs +++ b/packages/next-swc/crates/next-api/src/middleware.rs @@ -161,6 +161,7 @@ impl MiddlewareEndpoint { page: "/".to_string(), regions: None, matchers, + env: this.project.edge_env().await?.clone_value(), ..Default::default() }; let middleware_manifest_v2 = MiddlewaresManifestV2 { diff --git a/packages/next-swc/crates/next-api/src/pages.rs b/packages/next-swc/crates/next-api/src/pages.rs index b76e96322af53..e4e7595cc2bb4 100644 --- a/packages/next-swc/crates/next-api/src/pages.rs +++ b/packages/next-swc/crates/next-api/src/pages.rs @@ -1095,6 +1095,7 @@ impl PageEndpoint { page: original_name.to_string(), regions: None, matchers: vec![matchers], + env: this.pages_project.project().edge_env().await?.clone_value(), ..Default::default() }; let middleware_manifest_v2 = MiddlewaresManifestV2 { diff --git a/packages/next-swc/crates/next-api/src/project.rs b/packages/next-swc/crates/next-api/src/project.rs index ee0ee8333549c..9c9c9eaf05367 100644 --- a/packages/next-swc/crates/next-api/src/project.rs +++ b/packages/next-swc/crates/next-api/src/project.rs @@ -1,7 +1,7 @@ use std::path::MAIN_SEPARATOR; use anyhow::Result; -use indexmap::{map::Entry, IndexMap}; +use indexmap::{indexmap, map::Entry, IndexMap}; use next_core::{ all_assets_from_entries, app_structure::find_app_dir, @@ -68,6 +68,14 @@ use crate::{ versioned_content_map::{OutputAssetsOperation, VersionedContentMap}, }; +#[derive(Debug, Serialize, Deserialize, Clone, TaskInput, PartialEq, Eq, TraceRawVcs)] +#[serde(rename_all = "camelCase")] +pub struct DraftModeOptions { + pub preview_mode_id: String, + pub preview_mode_encryption_key: String, + pub preview_mode_signing_key: String, +} + #[derive(Debug, Serialize, Deserialize, Clone, TaskInput, PartialEq, Eq, TraceRawVcs)] #[serde(rename_all = "camelCase")] pub struct ProjectOptions { @@ -96,6 +104,15 @@ pub struct ProjectOptions { /// The mode in which Next.js is running. pub dev: bool, + + /// The server actions encryption key. + pub encryption_key: String, + + /// The build id. + pub build_id: String, + + /// Options for draft mode. + pub preview_props: DraftModeOptions, } #[derive(Debug, Serialize, Deserialize, Clone, TaskInput, PartialEq, Eq, TraceRawVcs)] @@ -126,6 +143,15 @@ pub struct PartialProjectOptions { /// The mode in which Next.js is running. pub dev: Option, + + /// The server actions encryption key. + pub encryption_key: Option, + + /// The build id. + pub build_id: Option, + + /// Options for draft mode. + pub preview_props: Option, } #[derive(Debug, Serialize, Deserialize, Clone, TaskInput, PartialEq, Eq, TraceRawVcs)] @@ -166,29 +192,55 @@ impl ProjectContainer { #[turbo_tasks::function] pub fn update(&self, options: PartialProjectOptions) -> Vc<()> { + let PartialProjectOptions { + root_path, + project_path, + next_config, + js_config, + env, + define_env, + watch, + dev, + encryption_key, + build_id, + preview_props, + } = options; + let mut new_options = self.options_state.get().clone(); - if let Some(root_path) = options.root_path { + if let Some(root_path) = root_path { new_options.root_path = root_path; } - if let Some(project_path) = options.project_path { + if let Some(project_path) = project_path { new_options.project_path = project_path; } - if let Some(next_config) = options.next_config { + if let Some(next_config) = next_config { new_options.next_config = next_config; } - if let Some(js_config) = options.js_config { + if let Some(js_config) = js_config { new_options.js_config = js_config; } - if let Some(env) = options.env { + if let Some(env) = env { new_options.env = env; } - if let Some(define_env) = options.define_env { + if let Some(define_env) = define_env { new_options.define_env = define_env; } - if let Some(watch) = options.watch { + if let Some(watch) = watch { new_options.watch = watch; } + if let Some(dev) = dev { + new_options.dev = dev; + } + if let Some(encryption_key) = encryption_key { + new_options.encryption_key = encryption_key; + } + if let Some(build_id) = build_id { + new_options.build_id = build_id; + } + if let Some(preview_props) = preview_props { + new_options.preview_props = preview_props; + } // TODO: Handle mode switch, should prevent mode being switched. @@ -201,32 +253,36 @@ impl ProjectContainer { pub async fn project(self: Vc) -> Result> { let this = self.await?; - let (env, define_env, next_config, js_config, root_path, project_path, watch, dev) = { + let env_map: Vc; + let next_config; + let define_env; + let js_config; + let root_path; + let project_path; + let watch; + let dev; + let encryption_key; + let build_id; + let preview_props; + { let options = this.options_state.get(); - let env: Vc = Vc::cell(options.env.iter().cloned().collect()); - let define_env: Vc = ProjectDefineEnv { + env_map = Vc::cell(options.env.iter().cloned().collect()); + define_env = ProjectDefineEnv { client: Vc::cell(options.define_env.client.iter().cloned().collect()), edge: Vc::cell(options.define_env.edge.iter().cloned().collect()), nodejs: Vc::cell(options.define_env.nodejs.iter().cloned().collect()), } .cell(); - let next_config = NextConfig::from_string(Vc::cell(options.next_config.clone())); - let js_config = JsConfig::from_string(Vc::cell(options.js_config.clone())); - let root_path = options.root_path.clone(); - let project_path = options.project_path.clone(); - let watch = options.watch; - let dev = options.dev; - ( - env, - define_env, - next_config, - js_config, - root_path, - project_path, - watch, - dev, - ) - }; + next_config = NextConfig::from_string(Vc::cell(options.next_config.clone())); + js_config = JsConfig::from_string(Vc::cell(options.js_config.clone())); + root_path = options.root_path.clone(); + project_path = options.project_path.clone(); + watch = options.watch; + dev = options.dev; + encryption_key = options.encryption_key.clone(); + build_id = options.build_id.clone(); + preview_props = options.preview_props.clone(); + } let dist_dir = next_config .await? @@ -241,7 +297,7 @@ impl ProjectContainer { next_config, js_config, dist_dir, - env: Vc::upcast(env), + env: Vc::upcast(env_map), define_env, browserslist_query: "last 1 Chrome versions, last 1 Firefox versions, last 1 Safari \ versions, last 1 Edge versions" @@ -252,6 +308,9 @@ impl ProjectContainer { NextMode::Build.cell() }, versioned_content_map: this.versioned_content_map, + build_id, + encryption_key, + preview_props, } .cell()) } @@ -323,6 +382,12 @@ pub struct Project { mode: Vc, versioned_content_map: Vc, + + build_id: String, + + encryption_key: String, + + preview_props: DraftModeOptions, } #[turbo_tasks::value] @@ -545,6 +610,18 @@ impl Project { )) } + #[turbo_tasks::function] + pub(super) fn edge_env(&self) -> Vc { + let edge_env = indexmap! { + "__NEXT_BUILD_ID".to_string() => self.build_id.clone(), + "NEXT_SERVER_ACTIONS_ENCRYPTION_KEY".to_string() => self.encryption_key.clone(), + "__NEXT_PREVIEW_MODE_ID".to_string() => self.preview_props.preview_mode_id.clone(), + "__NEXT_PREVIEW_MODE_ENCRYPTION_KEY".to_string() => self.preview_props.preview_mode_encryption_key.clone(), + "__NEXT_PREVIEW_MODE_SIGNING_KEY".to_string() => self.preview_props.preview_mode_signing_key.clone(), + }; + Vc::cell(edge_env) + } + #[turbo_tasks::function] pub(super) async fn client_chunking_context( self: Vc, diff --git a/packages/next-swc/crates/next-core/src/next_app/app_page_entry.rs b/packages/next-swc/crates/next-core/src/next_app/app_page_entry.rs index fdb7dc3193efe..77f4a1b14857c 100644 --- a/packages/next-swc/crates/next-core/src/next_app/app_page_entry.rs +++ b/packages/next-swc/crates/next-core/src/next_app/app_page_entry.rs @@ -152,7 +152,6 @@ async fn wrap_edge_page( let next_config = &*next_config.await?; // TODO(WEB-1824): add build support - let build_id = "development"; let dev = true; // TODO(timneutkens): remove this @@ -174,7 +173,6 @@ async fn wrap_edge_page( indexmap! { "VAR_USERLAND" => INNER.to_string(), "VAR_PAGE" => page.to_string(), - "VAR_BUILD_ID" => build_id.to_string(), }, indexmap! { "sriEnabled" => serde_json::Value::Bool(sri_enabled).to_string(), diff --git a/packages/next-swc/crates/next-core/src/next_manifests/mod.rs b/packages/next-swc/crates/next-core/src/next_manifests/mod.rs index 632e0cf051232..409078f8e0a00 100644 --- a/packages/next-swc/crates/next-core/src/next_manifests/mod.rs +++ b/packages/next-swc/crates/next-core/src/next_manifests/mod.rs @@ -4,7 +4,7 @@ pub(crate) mod client_reference_manifest; use std::collections::HashMap; -use indexmap::IndexSet; +use indexmap::{IndexMap, IndexSet}; use serde::{Deserialize, Serialize}; use turbo_tasks::{trace::TraceRawVcs, TaskInput}; @@ -88,6 +88,7 @@ pub struct EdgeFunctionDefinition { pub assets: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub regions: Option, + pub env: IndexMap, } #[derive(Serialize, Default, Debug)] diff --git a/packages/next-swc/crates/next-core/src/next_pages/page_entry.rs b/packages/next-swc/crates/next-core/src/next_pages/page_entry.rs index dbdfffaedac98..4800d5b772d4d 100644 --- a/packages/next-swc/crates/next-core/src/next_pages/page_entry.rs +++ b/packages/next-swc/crates/next-core/src/next_pages/page_entry.rs @@ -212,7 +212,6 @@ async fn wrap_edge_page( let next_config = &*next_config.await?; // TODO(WEB-1824): add build support - let build_id = "development"; let dev = true; let sri_enabled = !dev @@ -229,7 +228,6 @@ async fn wrap_edge_page( indexmap! { "VAR_USERLAND" => INNER.to_string(), "VAR_PAGE" => pathname.clone(), - "VAR_BUILD_ID" => build_id.to_string(), "VAR_MODULE_DOCUMENT" => INNER_DOCUMENT.to_string(), "VAR_MODULE_APP" => INNER_APP.to_string(), "VAR_MODULE_GLOBAL_ERROR" => INNER_ERROR.to_string(), diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 14b3fe444ec3f..33f294f3b6e4e 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -1393,6 +1393,9 @@ export default async function build( // TODO: Implement middlewareMatchers: undefined, }), + buildId: NextBuildContext.buildId!, + encryptionKey: NextBuildContext.encryptionKey!, + previewProps: NextBuildContext.previewProps!, }) await fs.mkdir(path.join(distDir, 'server'), { recursive: true }) diff --git a/packages/next/src/build/swc/index.ts b/packages/next/src/build/swc/index.ts index 92a1e4845fb5c..183605e4c1ba0 100644 --- a/packages/next/src/build/swc/index.ts +++ b/packages/next/src/build/swc/index.ts @@ -19,6 +19,7 @@ import { isDeepStrictEqual } from 'util' import type { DefineEnvPluginOptions } from '../webpack/plugins/define-env-plugin' import { getDefineEnv } from '../webpack/plugins/define-env-plugin' import type { PageExtensions } from '../page-extensions-type' +import type { __ApiPreviewProps } from '../../server/api-utils' const nextVersion = process.env.__NEXT_VERSION as string @@ -387,7 +388,6 @@ function logLoadFailure(attempts: any, triedWasm = false) { process.exit(1) }) } - export interface ProjectOptions { /** * A root path from which all files must be nested under. Trying to access @@ -432,6 +432,21 @@ export interface ProjectOptions { * The mode in which Next.js is running. */ dev: boolean + + /** + * The server actions encryption key. + */ + encryptionKey: string + + /** + * The build id. + */ + buildId: string + + /** + * Options for draft mode. + */ + previewProps: __ApiPreviewProps } type RustifiedEnv = { name: string; value: string }[] diff --git a/packages/next/src/build/webpack-build/impl.ts b/packages/next/src/build/webpack-build/impl.ts index 452060269102c..39ff87a40a0b4 100644 --- a/packages/next/src/build/webpack-build/impl.ts +++ b/packages/next/src/build/webpack-build/impl.ts @@ -152,7 +152,14 @@ export async function webpackBuildImpl( middlewareMatchers: entrypoints.middlewareMatchers, compilerType: COMPILER_NAMES.edgeServer, entrypoints: entrypoints.edgeServer, - edgePreviewProps: NextBuildContext.previewProps!, + edgePreviewProps: { + __NEXT_PREVIEW_MODE_ID: + NextBuildContext.previewProps!.previewModeId, + __NEXT_PREVIEW_MODE_ENCRYPTION_KEY: + NextBuildContext.previewProps!.previewModeEncryptionKey, + __NEXT_PREVIEW_MODE_SIGNING_KEY: + NextBuildContext.previewProps!.previewModeSigningKey, + }, ...info, }), ]) diff --git a/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts b/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts index acbceaf96fa63..e2163b023a09a 100644 --- a/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts +++ b/packages/next/src/build/webpack/loaders/next-edge-ssr-loader/index.ts @@ -143,7 +143,6 @@ const edgeSSRLoader: webpack.LoaderDefinitionFunction = { VAR_USERLAND: pageModPath, VAR_PAGE: page, - // VAR_BUILD_ID: buildId, }, { sriEnabled: JSON.stringify(sriEnabled), @@ -165,7 +164,6 @@ const edgeSSRLoader: webpack.LoaderDefinitionFunction = { VAR_USERLAND: pageModPath, VAR_PAGE: page, - // VAR_BUILD_ID: buildId, VAR_MODULE_DOCUMENT: documentPath, VAR_MODULE_APP: appPath, VAR_MODULE_GLOBAL_ERROR: errorPath, diff --git a/packages/next/src/server/dev/hot-reloader-turbopack.ts b/packages/next/src/server/dev/hot-reloader-turbopack.ts index 3220403c2a1cb..d7b8b5d7a2851 100644 --- a/packages/next/src/server/dev/hot-reloader-turbopack.ts +++ b/packages/next/src/server/dev/hot-reloader-turbopack.ts @@ -122,6 +122,7 @@ export async function createHotReloaderTurbopack( // of the current `next dev` invocation. hotReloaderSpan.stop() + const encryptionKey = await generateEncryptionKeyBase64() const project = await bindings.turbo.createProject({ projectPath: dir, rootPath: opts.nextConfig.experimental.outputFileTracingRoot || dir, @@ -142,6 +143,9 @@ export async function createHotReloaderTurbopack( // TODO: Implement middlewareMatchers: undefined, }), + buildId: 'development', + encryptionKey, + previewProps: opts.fsChecker.prerenderManifest.preview, }) const entrypointsSubscription = project.entrypointsSubscribe() @@ -165,7 +169,7 @@ export async function createHotReloaderTurbopack( const manifestLoader = new TurbopackManifestLoader({ buildId, distDir, - encryptionKey: await generateEncryptionKeyBase64(), + encryptionKey: encryptionKey, }) // Dev specific diff --git a/test/development/basic/next-rs-api.test.ts b/test/development/basic/next-rs-api.test.ts index ce1137a93153f..cde9ab66700ec 100644 --- a/test/development/basic/next-rs-api.test.ts +++ b/test/development/basic/next-rs-api.test.ts @@ -218,6 +218,13 @@ describe('next.rs api', () => { hasRewrites: false, middlewareMatchers: undefined, }), + buildId: 'development', + encryptionKey: '12345', + previewProps: { + previewModeId: 'development', + previewModeEncryptionKey: '12345', + previewModeSigningKey: '12345', + }, }) projectUpdateSubscription = filterMapAsyncIterator( project.updateInfoSubscribe(1000), From 64b592fef43549563d2479225c71bbc164881398 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Fri, 3 May 2024 10:46:30 +0200 Subject: [PATCH 09/14] add dev flag and fix test --- .../next/src/server/dev/hot-reloader-turbopack.ts | 6 +++--- test/e2e/middleware-general/test/index.test.ts | 13 +++++++------ .../middleware-trailing-slash/test/index.test.ts | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/next/src/server/dev/hot-reloader-turbopack.ts b/packages/next/src/server/dev/hot-reloader-turbopack.ts index d7b8b5d7a2851..f6409ae24184f 100644 --- a/packages/next/src/server/dev/hot-reloader-turbopack.ts +++ b/packages/next/src/server/dev/hot-reloader-turbopack.ts @@ -122,7 +122,7 @@ export async function createHotReloaderTurbopack( // of the current `next dev` invocation. hotReloaderSpan.stop() - const encryptionKey = await generateEncryptionKeyBase64() + const encryptionKey = await generateEncryptionKeyBase64(true) const project = await bindings.turbo.createProject({ projectPath: dir, rootPath: opts.nextConfig.experimental.outputFileTracingRoot || dir, @@ -143,7 +143,7 @@ export async function createHotReloaderTurbopack( // TODO: Implement middlewareMatchers: undefined, }), - buildId: 'development', + buildId, encryptionKey, previewProps: opts.fsChecker.prerenderManifest.preview, }) @@ -169,7 +169,7 @@ export async function createHotReloaderTurbopack( const manifestLoader = new TurbopackManifestLoader({ buildId, distDir, - encryptionKey: encryptionKey, + encryptionKey, }) // Dev specific diff --git a/test/e2e/middleware-general/test/index.test.ts b/test/e2e/middleware-general/test/index.test.ts index 95831f2cd67ca..c8100fc95952b 100644 --- a/test/e2e/middleware-general/test/index.test.ts +++ b/test/e2e/middleware-general/test/index.test.ts @@ -170,9 +170,9 @@ describe('Middleware Runtime', () => { ...manifest.middleware['/'], } const envs = { - ...middlewareWithoutEnvs.environments, + ...middlewareWithoutEnvs.env, } - delete middlewareWithoutEnvs.environments + delete middlewareWithoutEnvs.env expect(middlewareWithoutEnvs).toEqual({ files: expect.arrayContaining([ 'server/edge-runtime-webpack.js', @@ -186,10 +186,11 @@ describe('Middleware Runtime', () => { regions: 'auto', }) expect(envs).toContainAllKeys([ - 'buildId', - 'previewModeEncryptionKey', - 'previewModeId', - 'previewModeSigningKey', + 'NEXT_SERVER_ACTIONS_ENCRYPTION_KEY', + '__NEXT_BUILD_ID', + '__NEXT_PREVIEW_MODE_ENCRYPTION_KEY', + '__NEXT_PREVIEW_MODE_ID', + '__NEXT_PREVIEW_MODE_SIGNING_KEY', ]) }) diff --git a/test/e2e/middleware-trailing-slash/test/index.test.ts b/test/e2e/middleware-trailing-slash/test/index.test.ts index e6f04754930f3..1ee913f5a5578 100644 --- a/test/e2e/middleware-trailing-slash/test/index.test.ts +++ b/test/e2e/middleware-trailing-slash/test/index.test.ts @@ -112,7 +112,7 @@ describe('Middleware Runtime trailing slash', () => { const middlewareWithoutEnvs = { ...manifest.middleware['/'], } - delete middlewareWithoutEnvs.environments + delete middlewareWithoutEnvs.env expect(middlewareWithoutEnvs).toEqual({ files: expect.arrayContaining([ 'prerender-manifest.js', From 42c8b10b7a4d8366cf50d5d4287df5cdb20734d4 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Fri, 3 May 2024 17:36:13 +0200 Subject: [PATCH 10/14] fix edge build manifest --- .../webpack/plugins/build-manifest-plugin.ts | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts b/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts index 18c45d3971026..9be30ecee2499 100644 --- a/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts +++ b/packages/next/src/build/webpack/plugins/build-manifest-plugin.ts @@ -29,32 +29,36 @@ export type ClientBuildManifest = { // generated). export const srcEmptySsgManifest = `self.__SSG_MANIFEST=new Set;self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()` -// Return different path for edge runtime and nodejs runtime -// edge: '"/static/" + process.env.__NEXT_BUILD_ID + "/low-priority.js"' // nodejs: '/static//low-priority.js' -function buildLowPriorityPath( - filename: string, - buildId: string, - isEdgeRuntime: boolean -) { - return isEdgeRuntime - ? `"${CLIENT_STATIC_FILES_PATH}/" + process.env.__NEXT_BUILD_ID + "/${filename}"` - : `${CLIENT_STATIC_FILES_PATH}/${buildId}/${filename}` +function buildNodejsLowPriorityPath(filename: string, buildId: string) { + return `${CLIENT_STATIC_FILES_PATH}/${buildId}/${filename}` } -function createEdgeRuntimeManifest( - originAssetMap: BuildManifest, - buildId: string -): string { - const assetMap = { +function createEdgeRuntimeManifest(originAssetMap: BuildManifest): string { + const manifestFilenames = ['_buildManifest.js', '_ssgManifest.js'] + + const assetMap: BuildManifest = { ...originAssetMap, - lowPriorityFiles: [ - buildLowPriorityPath('_buildManifest.js', buildId, true), - buildLowPriorityPath('_ssgManifest.js', buildId, true), - ], + lowPriorityFiles: [], } - return JSON.stringify(assetMap, null, 2) + const manifestDefCode = `self.__BUILD_MANIFEST = ${JSON.stringify( + assetMap, + null, + 2 + )};\n` + // edge lowPriorityFiles item: '"/static/" + process.env.__NEXT_BUILD_ID + "/low-priority.js"'. + // Since lowPriorityFiles is not fixed and relying on `process.env.__NEXT_BUILD_ID`, we'll produce code creating it dynamically. + const lowPriorityFilesCode = + `self.__BUILD_MANIFEST.lowPriorityFiles = [\n` + + manifestFilenames + .map((filename) => { + return `"/static/" + process.env.__NEXT_BUILD_ID + "/${filename}",\n` + }) + .join(',') + + `\n];` + + return manifestDefCode + lowPriorityFilesCode } function normalizeRewrite(item: { @@ -259,15 +263,13 @@ export default class BuildManifestPlugin { // Add the runtime build manifest file (generated later in this file) // as a dependency for the app. If the flag is false, the file won't be // downloaded by the client. - const buildManifestPath = buildLowPriorityPath( + const buildManifestPath = buildNodejsLowPriorityPath( '_buildManifest.js', - this.buildId, - false + this.buildId ) - const ssgManifestPath = buildLowPriorityPath( + const ssgManifestPath = buildNodejsLowPriorityPath( '_ssgManifest.js', - this.buildId, - false + this.buildId ) assetMap.lowPriorityFiles.push(buildManifestPath, ssgManifestPath) assets[ssgManifestPath] = new sources.RawSource(srcEmptySsgManifest) @@ -292,10 +294,7 @@ export default class BuildManifestPlugin { ) assets[`server/${MIDDLEWARE_BUILD_MANIFEST}.js`] = new sources.RawSource( - `self.__BUILD_MANIFEST=${createEdgeRuntimeManifest( - assetMap, - this.buildId - )}` + `${createEdgeRuntimeManifest(assetMap)}` ) if (!this.isDevFallback) { From ab230ec27ed2e450de0f6974b9a75245109931d4 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Fri, 3 May 2024 18:33:45 +0200 Subject: [PATCH 11/14] add determinstic build --- .../app/app-page/edge/page.js | 5 ++ .../deterministic-build/app/app-page/page.js | 3 ++ .../app/app-route/edge/route.js | 5 ++ .../app/app-route/route.js | 5 ++ .../deterministic-build/app/layout.js | 9 ++++ .../deterministic-build/index.test.ts | 48 +++++++++++++++++++ .../pages/api/pages-api/edge/index.js | 5 ++ .../pages/api/pages-api/index.js | 3 ++ .../pages/pages-page/edge/index.js | 5 ++ .../pages/pages-page/index.js | 8 ++++ 10 files changed, 96 insertions(+) create mode 100644 test/production/deterministic-build/app/app-page/edge/page.js create mode 100644 test/production/deterministic-build/app/app-page/page.js create mode 100644 test/production/deterministic-build/app/app-route/edge/route.js create mode 100644 test/production/deterministic-build/app/app-route/route.js create mode 100644 test/production/deterministic-build/app/layout.js create mode 100644 test/production/deterministic-build/index.test.ts create mode 100644 test/production/deterministic-build/pages/api/pages-api/edge/index.js create mode 100644 test/production/deterministic-build/pages/api/pages-api/index.js create mode 100644 test/production/deterministic-build/pages/pages-page/edge/index.js create mode 100644 test/production/deterministic-build/pages/pages-page/index.js diff --git a/test/production/deterministic-build/app/app-page/edge/page.js b/test/production/deterministic-build/app/app-page/edge/page.js new file mode 100644 index 0000000000000..4d6fdccfface9 --- /dev/null +++ b/test/production/deterministic-build/app/app-page/edge/page.js @@ -0,0 +1,5 @@ +export default function Page() { + return 'app-page (edge)' +} + +export const runtime = 'edge' diff --git a/test/production/deterministic-build/app/app-page/page.js b/test/production/deterministic-build/app/app-page/page.js new file mode 100644 index 0000000000000..ad1654219282e --- /dev/null +++ b/test/production/deterministic-build/app/app-page/page.js @@ -0,0 +1,3 @@ +export default function Page() { + return 'app-page (node)' +} diff --git a/test/production/deterministic-build/app/app-route/edge/route.js b/test/production/deterministic-build/app/app-route/edge/route.js new file mode 100644 index 0000000000000..fc4a0205deaab --- /dev/null +++ b/test/production/deterministic-build/app/app-route/edge/route.js @@ -0,0 +1,5 @@ +export function GET() { + return new Response('app-route (edge)') +} + +export const runtime = 'edge' diff --git a/test/production/deterministic-build/app/app-route/route.js b/test/production/deterministic-build/app/app-route/route.js new file mode 100644 index 0000000000000..b01445578f89d --- /dev/null +++ b/test/production/deterministic-build/app/app-route/route.js @@ -0,0 +1,5 @@ +export function GET() { + return new Response('app-route (node)') +} + +export const dynamic = 'force-dynamic' diff --git a/test/production/deterministic-build/app/layout.js b/test/production/deterministic-build/app/layout.js new file mode 100644 index 0000000000000..ff05382bcc3f1 --- /dev/null +++ b/test/production/deterministic-build/app/layout.js @@ -0,0 +1,9 @@ +export default function Layout({ children }) { + return ( + + {children} + + ) +} + +export const dynamic = 'force-dynamic' diff --git a/test/production/deterministic-build/index.test.ts b/test/production/deterministic-build/index.test.ts new file mode 100644 index 0000000000000..40a3516fb6bbc --- /dev/null +++ b/test/production/deterministic-build/index.test.ts @@ -0,0 +1,48 @@ +import crypto from 'crypto' +import { nextTestSetup } from 'e2e-utils' +import { nextBuild } from 'next-test-utils' + +function generateMD5(text: string) { + const hash = crypto.createHash('md5') + hash.update(text) + return hash.digest('hex') +} + +const filePaths = [ + 'app/app-page/page', + 'app/app-page/edge/page', + 'app/app-route/route', + 'app/app-route/edge/route', + 'pages/api/pages-api', + 'pages/api/pages-api/edge', + 'pages/pages-page', + 'pages/pages-page/edge', +] + +describe('deterministic build', () => { + const { next } = nextTestSetup({ + files: __dirname, + skipStart: true, + }) + + const firstBuildFileMd5Hashes: Record = {} + const secondBuildFileMd5Hashes: Record = {} + + beforeAll(async () => { + await nextBuild(__dirname, []) + for (const file of filePaths) { + const content = await next.readFile(`.next/server/${file}.js`) + firstBuildFileMd5Hashes[file] = generateMD5(content) + } + + await nextBuild(__dirname, []) + for (const file of filePaths) { + const content = await next.readFile(`.next/server/${file}.js`) + secondBuildFileMd5Hashes[file] = generateMD5(content) + } + }) + + it('should have same md5 file across build', async () => { + expect(firstBuildFileMd5Hashes).toEqual(secondBuildFileMd5Hashes) + }) +}) diff --git a/test/production/deterministic-build/pages/api/pages-api/edge/index.js b/test/production/deterministic-build/pages/api/pages-api/edge/index.js new file mode 100644 index 0000000000000..670361403e996 --- /dev/null +++ b/test/production/deterministic-build/pages/api/pages-api/edge/index.js @@ -0,0 +1,5 @@ +export default function handler() { + return new Response('pages-api (edge)') +} + +export const runtime = 'experimental-edge' diff --git a/test/production/deterministic-build/pages/api/pages-api/index.js b/test/production/deterministic-build/pages/api/pages-api/index.js new file mode 100644 index 0000000000000..49c3323d5c1a5 --- /dev/null +++ b/test/production/deterministic-build/pages/api/pages-api/index.js @@ -0,0 +1,3 @@ +export default function handler(req, res) { + res.send('pages-api (node)') +} diff --git a/test/production/deterministic-build/pages/pages-page/edge/index.js b/test/production/deterministic-build/pages/pages-page/edge/index.js new file mode 100644 index 0000000000000..a227e8796424f --- /dev/null +++ b/test/production/deterministic-build/pages/pages-page/edge/index.js @@ -0,0 +1,5 @@ +export default function Page() { + return 'pages-page (edge)' +} + +export const runtime = 'experimental-edge' diff --git a/test/production/deterministic-build/pages/pages-page/index.js b/test/production/deterministic-build/pages/pages-page/index.js new file mode 100644 index 0000000000000..fcfcc0a629ec7 --- /dev/null +++ b/test/production/deterministic-build/pages/pages-page/index.js @@ -0,0 +1,8 @@ +export default function Page() { + return 'pages-page (node)' +} + +// Use gssp to opt-in dynamic rendering +export function getServerSideProps() { + return { props: {} } +} From e13df6d514cd65ef023e6c3b4bd1e2d92fe1c158 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Fri, 3 May 2024 18:50:24 +0200 Subject: [PATCH 12/14] skip on turbopack build --- test/turbopack-build-tests-manifest.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/turbopack-build-tests-manifest.json b/test/turbopack-build-tests-manifest.json index 38e08bbbba840..8cd207745a7d0 100644 --- a/test/turbopack-build-tests-manifest.json +++ b/test/turbopack-build-tests-manifest.json @@ -14305,6 +14305,15 @@ "flakey": [], "runtimeError": false }, + "test/production/deterministic-build/index.test.ts": { + "passed": [], + "failed": [ + "deterministic build > should have same md5 file across build" + ], + "pending": [], + "flakey": [], + "runtimeError": false + }, "test/production/app-dir-edge-runtime-with-wasm/index.test.ts": { "passed": ["app-dir edge runtime with wasm should have built"], "failed": [], From 3c9be21ec8fbc6598c06193459a379965086156e Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Sun, 5 May 2024 21:55:21 +0200 Subject: [PATCH 13/14] add more test --- .../deterministic-build/index.test.ts | 47 ++++++++++++++----- test/turbopack-build-tests-manifest.json | 4 +- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/test/production/deterministic-build/index.test.ts b/test/production/deterministic-build/index.test.ts index 40a3516fb6bbc..677690c370831 100644 --- a/test/production/deterministic-build/index.test.ts +++ b/test/production/deterministic-build/index.test.ts @@ -1,5 +1,5 @@ import crypto from 'crypto' -import { nextTestSetup } from 'e2e-utils' +import { NextInstance, nextTestSetup } from 'e2e-utils' import { nextBuild } from 'next-test-utils' function generateMD5(text: string) { @@ -8,41 +8,62 @@ function generateMD5(text: string) { return hash.digest('hex') } -const filePaths = [ +const nodeFilePaths = [ 'app/app-page/page', - 'app/app-page/edge/page', 'app/app-route/route', - 'app/app-route/edge/route', 'pages/api/pages-api', - 'pages/api/pages-api/edge', 'pages/pages-page', - 'pages/pages-page/edge', ] +async function getEdgeRouteFilesFromManifest(next: NextInstance) { + const manifest: any = JSON.parse( + await next.readFile('.next/server/middleware-manifest.json') + ) + const routeKeys = Object.keys(manifest.functions) + const md5Map: Record = {} + for (const route of routeKeys) { + const files: string[] = manifest.functions[route].files + const filesMd5Promises = files.map(async (filePath: string) => { + const content = await next.readFile(`.next/${filePath}`) + return generateMD5(content) + }) + const md5s = await Promise.all(filesMd5Promises) + md5Map[route] = md5s + } + return md5Map +} + describe('deterministic build', () => { const { next } = nextTestSetup({ files: __dirname, skipStart: true, }) - const firstBuildFileMd5Hashes: Record = {} - const secondBuildFileMd5Hashes: Record = {} + // Edge - { [route]: [file md5s] } + const edgeBuildFileMd5Hashes: Record[] = [] + // Node - { [route]: page.js or route.js md5 } + const nodeBuildFileMd5Hashes: Record[] = [{}, {}] beforeAll(async () => { + // First build await nextBuild(__dirname, []) - for (const file of filePaths) { + edgeBuildFileMd5Hashes.push(await getEdgeRouteFilesFromManifest(next)) + for (const file of nodeFilePaths) { const content = await next.readFile(`.next/server/${file}.js`) - firstBuildFileMd5Hashes[file] = generateMD5(content) + nodeBuildFileMd5Hashes[0][file] = generateMD5(content) } + // Second build await nextBuild(__dirname, []) - for (const file of filePaths) { + edgeBuildFileMd5Hashes.push(await getEdgeRouteFilesFromManifest(next)) + for (const file of nodeFilePaths) { const content = await next.readFile(`.next/server/${file}.js`) - secondBuildFileMd5Hashes[file] = generateMD5(content) + nodeBuildFileMd5Hashes[1][file] = generateMD5(content) } }) it('should have same md5 file across build', async () => { - expect(firstBuildFileMd5Hashes).toEqual(secondBuildFileMd5Hashes) + expect(edgeBuildFileMd5Hashes[0]).toEqual(edgeBuildFileMd5Hashes[1]) + expect(nodeBuildFileMd5Hashes[0]).toEqual(nodeBuildFileMd5Hashes[1]) }) }) diff --git a/test/turbopack-build-tests-manifest.json b/test/turbopack-build-tests-manifest.json index 8cd207745a7d0..d4d797cb5f2d7 100644 --- a/test/turbopack-build-tests-manifest.json +++ b/test/turbopack-build-tests-manifest.json @@ -14307,9 +14307,7 @@ }, "test/production/deterministic-build/index.test.ts": { "passed": [], - "failed": [ - "deterministic build > should have same md5 file across build" - ], + "failed": ["deterministic build should have same md5 file across build"], "pending": [], "flakey": [], "runtimeError": false From b74e8593ffc78df7d24ea46020b92d8db23350bb Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Mon, 6 May 2024 15:58:20 +0200 Subject: [PATCH 14/14] update test --- test/production/deterministic-build/index.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/production/deterministic-build/index.test.ts b/test/production/deterministic-build/index.test.ts index 677690c370831..c9bea7107803f 100644 --- a/test/production/deterministic-build/index.test.ts +++ b/test/production/deterministic-build/index.test.ts @@ -1,6 +1,5 @@ import crypto from 'crypto' import { NextInstance, nextTestSetup } from 'e2e-utils' -import { nextBuild } from 'next-test-utils' function generateMD5(text: string) { const hash = crypto.createHash('md5') @@ -46,7 +45,7 @@ describe('deterministic build', () => { beforeAll(async () => { // First build - await nextBuild(__dirname, []) + await next.build() edgeBuildFileMd5Hashes.push(await getEdgeRouteFilesFromManifest(next)) for (const file of nodeFilePaths) { const content = await next.readFile(`.next/server/${file}.js`) @@ -54,7 +53,7 @@ describe('deterministic build', () => { } // Second build - await nextBuild(__dirname, []) + await next.build() edgeBuildFileMd5Hashes.push(await getEdgeRouteFilesFromManifest(next)) for (const file of nodeFilePaths) { const content = await next.readFile(`.next/server/${file}.js`)