From ec742f70e150381d094636eff815f40f22c5d168 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Mon, 27 Feb 2023 21:09:54 -0500 Subject: [PATCH] Update Middleware Routing (#3930) This accomplishes 2 things: - Moves the creation of the edge info onto the Next.js side - Extracts the middleware's `export const config = {}`, so that we can know what matchers are needed before invoking the edge function definition. After this, an update to the Next.js side will enable middleware. Fixes WEB-623 --- .../next-core/js/src/entry/edge-bootstrap.ts | 3 +- crates/next-core/js/src/entry/router.ts | 18 ++---- crates/next-core/src/app_source.rs | 1 + crates/next-core/src/next_edge/transition.rs | 11 +++- crates/next-core/src/page_source.rs | 1 + crates/next-core/src/router.rs | 58 ++++++++++++++----- crates/next-core/src/util.rs | 47 ++++++++++++++- ... Error evaluating Node.js code-be8dbe.txt} | 2 +- 8 files changed, 108 insertions(+), 33 deletions(-) rename crates/next-dev-tests/tests/integration/turbopack/basic/comptime/issues/{Error evaluating Node.js code-b780fa.txt => Error evaluating Node.js code-be8dbe.txt} (79%) diff --git a/crates/next-core/js/src/entry/edge-bootstrap.ts b/crates/next-core/js/src/entry/edge-bootstrap.ts index 6acd3668934fb..0ad3d2c9baf81 100644 --- a/crates/next-core/js/src/entry/edge-bootstrap.ts +++ b/crates/next-core/js/src/entry/edge-bootstrap.ts @@ -1,3 +1,4 @@ +declare const NAME: string; declare const PAGE: string; import { adapter, enhanceGlobals } from "next/dist/server/web/adapter"; @@ -15,7 +16,7 @@ if (typeof handler !== "function") { // @ts-ignore globalThis._ENTRIES = { - middleware_edge: { + [`middleware_${NAME}`]: { default: function (opts: any) { return adapter({ ...opts, diff --git a/crates/next-core/js/src/entry/router.ts b/crates/next-core/js/src/entry/router.ts index a5fec22528e21..c55c9c9803db9 100644 --- a/crates/next-core/js/src/entry/router.ts +++ b/crates/next-core/js/src/entry/router.ts @@ -1,16 +1,16 @@ import type { Ipc } from "@vercel/turbopack-next/ipc/index"; import type { IncomingMessage, ServerResponse } from "node:http"; import { Buffer } from "node:buffer"; -import { join } from "node:path"; import { createServer, makeRequest } from "@vercel/turbopack-next/ipc/server"; import { toPairs } from "@vercel/turbopack-next/internal/headers"; -import { makeResolver } from "next/dist/server/router.js"; +import { makeResolver } from "next/dist/server/lib/route-resolver"; import loadConfig from "next/dist/server/config"; import { PHASE_DEVELOPMENT_SERVER } from "next/dist/shared/lib/constants"; import "next/dist/server/node-polyfill-fetch.js"; import middlewareChunkGroup from "MIDDLEWARE_CHUNK_GROUP"; +import middlewareConfig from "MIDDLEWARE_CONFIG"; type RouterRequest = { method: string; @@ -74,16 +74,10 @@ async function getResolveRoute( true ); - const edgeInfo = { - name: "edge", - paths: middlewareChunkGroup.map((chunk: string) => - join(process.cwd(), chunk) - ), - wasm: [], - env: [], - assets: [], - }; - return await makeResolver(dir, nextConfig, edgeInfo); + return await makeResolver(dir, nextConfig, { + files: middlewareChunkGroup, + matcher: middlewareConfig.matcher, + }); } export default async function route( diff --git a/crates/next-core/src/app_source.rs b/crates/next-core/src/app_source.rs index 76c4839a09a6b..29c4890d1abc6 100644 --- a/crates/next-core/src/app_source.rs +++ b/crates/next-core/src/app_source.rs @@ -202,6 +202,7 @@ fn next_route_transition( output_path, base_path: app_dir, bootstrap_file: next_js_file("entry/app/route-bootstrap.ts"), + entry_name: "edge".to_string(), } .cell() .into() diff --git a/crates/next-core/src/next_edge/transition.rs b/crates/next-core/src/next_edge/transition.rs index e8fe9841f8b5a..242e91e7c3e8a 100644 --- a/crates/next-core/src/next_edge/transition.rs +++ b/crates/next-core/src/next_edge/transition.rs @@ -29,6 +29,7 @@ pub struct NextEdgeTransition { pub output_path: FileSystemPathVc, pub base_path: FileSystemPathVc, pub bootstrap_file: FileContentVc, + pub entry_name: String, } #[turbo_tasks::value_impl] @@ -81,8 +82,14 @@ impl Transition for NextEdgeTransition { } else { path }; - let mut new_content = - RopeBuilder::from(format!("const PAGE = {};\n", stringify_js(path)).into_bytes()); + let mut new_content = RopeBuilder::from( + format!( + "const NAME={};\nconst PAGE = {};\n", + stringify_js(&self.entry_name), + stringify_js(path) + ) + .into_bytes(), + ); new_content.concat(base.content()); let file = File::from(new_content.build()); let virtual_asset = VirtualAssetVc::new( diff --git a/crates/next-core/src/page_source.rs b/crates/next-core/src/page_source.rs index ef646eedff578..52974f1bce203 100644 --- a/crates/next-core/src/page_source.rs +++ b/crates/next-core/src/page_source.rs @@ -155,6 +155,7 @@ pub async fn create_page_source( output_path, base_path: project_path, bootstrap_file: next_js_file("entry/edge-bootstrap.ts"), + entry_name: "edge".to_string(), } .cell() .into(); diff --git a/crates/next-core/src/router.rs b/crates/next-core/src/router.rs index 64ad9f628a194..cdb49e6e09b22 100644 --- a/crates/next-core/src/router.rs +++ b/crates/next-core/src/router.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use anyhow::{bail, Result}; use serde::Deserialize; +use serde_json::json; use turbo_tasks::{ primitives::{JsonValueVc, StringsVc}, CompletionVc, Value, @@ -40,6 +41,7 @@ use crate::{ }, next_import_map::get_next_build_import_map, next_server::context::ServerContextType, + util::{parse_config_from_source, NextSourceConfigVc}, }; #[turbo_tasks::function] @@ -194,26 +196,49 @@ async fn config_assets( // is no middleware file, then we need to generate a default empty manifest // and we cannot process it with the next-edge transition because it // requires a real file for some reason. - let middleware_manifest = match &*middleware_config { - Some(c) => context.with_transition("next-edge").process( - c.as_asset(), - Value::new(ReferenceType::EcmaScriptModules( - EcmaScriptModulesReferenceSubType::Undefined, - )), - ), - None => as_es_module_asset( - VirtualAssetVc::new( - project_path.join("middleware.js"), - File::from("export default [];").into(), + let (manifest, config) = match &*middleware_config { + Some(c) => { + let manifest = context.with_transition("next-edge").process( + c.as_asset(), + Value::new(ReferenceType::EcmaScriptModules( + EcmaScriptModulesReferenceSubType::Undefined, + )), + ); + let config = parse_config_from_source(c.as_asset()); + (manifest, config) + } + None => { + let manifest = as_es_module_asset( + VirtualAssetVc::new( + project_path.join("middleware.js"), + File::from("export default [];").into(), + ) + .as_asset(), + context, ) - .as_asset(), - context, + .as_asset(); + let config = NextSourceConfigVc::default(); + (manifest, config) + } + }; + + let config_asset = as_es_module_asset( + VirtualAssetVc::new( + project_path.join("middleware_config.js"), + File::from(format!( + "export default {};", + json!({ "matcher": &config.await?.matcher }) + )) + .into(), ) .as_asset(), - }; + context, + ) + .as_asset(); let mut inner = HashMap::new(); - inner.insert("MIDDLEWARE_CHUNK_GROUP".to_string(), middleware_manifest); + inner.insert("MIDDLEWARE_CHUNK_GROUP".to_string(), manifest); + inner.insert("MIDDLEWARE_CONFIG".to_string(), config_asset); Ok(InnerAssetsVc::cell(inner)) } @@ -262,9 +287,10 @@ fn edge_transition_map( edge_compile_time_info, edge_chunking_context, edge_resolve_options_context, - output_path, + output_path: output_path.root(), base_path: project_path, bootstrap_file: next_js_file("entry/edge-bootstrap.ts"), + entry_name: "middleware".to_string(), } .cell() .into(); diff --git a/crates/next-core/src/util.rs b/crates/next-core/src/util.rs index fb37acc83dd51..1a60ba4f8a310 100644 --- a/crates/next-core/src/util.rs +++ b/crates/next-core/src/util.rs @@ -89,6 +89,17 @@ pub enum NextRuntime { #[derive(Default)] pub struct NextSourceConfig { pub runtime: NextRuntime, + + /// Middleware router matchers + pub matcher: Option>, +} + +#[turbo_tasks::value_impl] +impl NextSourceConfigVc { + #[turbo_tasks::function] + pub fn default() -> Self { + NextSourceConfig::default().cell() + } } /// An issue that occurred while resolving the React Refresh runtime module. @@ -179,7 +190,7 @@ pub async fn parse_config_from_source(module_asset: AssetVc) -> Result NextSourceConfig { @@ -229,6 +240,40 @@ fn parse_config_from_js_value(module_asset: AssetVc, value: &JsValue) -> NextSou ); } } + if key == "matcher" { + let mut matchers = vec![]; + match value { + JsValue::Constant(matcher) => { + if let Some(matcher) = matcher.as_str() { + matchers.push(matcher.to_string()); + } else { + invalid_config( + "The matcher property must be a string or array of \ + strings", + value, + ); + } + } + JsValue::Array { items, .. } => { + for item in items { + if let Some(matcher) = item.as_str() { + matchers.push(matcher.to_string()); + } else { + invalid_config( + "The matcher property must be a string or array \ + of strings", + value, + ); + } + } + } + _ => invalid_config( + "The matcher property must be a string or array of strings", + value, + ), + } + config.matcher = Some(matchers); + } } else { invalid_config( "The exported config object must not contain non-constant strings.", diff --git a/crates/next-dev-tests/tests/integration/turbopack/basic/comptime/issues/Error evaluating Node.js code-b780fa.txt b/crates/next-dev-tests/tests/integration/turbopack/basic/comptime/issues/Error evaluating Node.js code-be8dbe.txt similarity index 79% rename from crates/next-dev-tests/tests/integration/turbopack/basic/comptime/issues/Error evaluating Node.js code-b780fa.txt rename to crates/next-dev-tests/tests/integration/turbopack/basic/comptime/issues/Error evaluating Node.js code-be8dbe.txt index 726fe7cc5ae68..1810b3fe57cb9 100644 --- a/crates/next-dev-tests/tests/integration/turbopack/basic/comptime/issues/Error evaluating Node.js code-b780fa.txt +++ b/crates/next-dev-tests/tests/integration/turbopack/basic/comptime/issues/Error evaluating Node.js code-be8dbe.txt @@ -3,7 +3,7 @@ PlainIssue { context: "[project]/crates/next-dev-tests/tests/integration/turbopack/basic/comptime/input", category: "build", title: "Error evaluating Node.js code", - description: "Error: > Couldn't find a `pages` directory. Please create one under the project root\n at Object.findPagesDir (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/lib/find-pages-dir.js:86:19)\n at DevServer.getRoutes (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/server/dev/next-dev-server.js:130:59)\n at new Server (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/server/base-server.js:108:47)\n at new NextNodeServer (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/server/next-server.js:69:9)\n at new DevServer (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/server/dev/next-dev-server.js:96:9)\n at Object.makeResolver (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/server/lib/route-resolver.js:39:23)\n at getResolveRoute (crates/next-dev-tests/tests/integration/turbopack/basic/comptime/input/.next/build/router/chunks/router.js:170:97)\n at async Module.route (crates/next-dev-tests/tests/integration/turbopack/basic/comptime/input/.next/build/router/chunks/router.js:173:36)\n at async Module.run (crates/next-dev-tests/tests/integration/turbopack/basic/comptime/input/.next/build/router/chunks/[turbopack-node]_ipc_evaluate.ts._.js:142:39)\n", + description: "Error: > Couldn't find a `pages` directory. Please create one under the project root\n at Object.findPagesDir (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/lib/find-pages-dir.js:86:19)\n at DevServer.getRoutes (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/server/dev/next-dev-server.js:130:59)\n at new Server (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/server/base-server.js:108:47)\n at new NextNodeServer (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/server/next-server.js:69:9)\n at new DevServer (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/server/dev/next-dev-server.js:96:9)\n at Object.makeResolver (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/server/lib/route-resolver.js:39:23)\n at getResolveRoute (crates/next-dev-tests/tests/integration/turbopack/basic/comptime/input/.next/build/router/chunks/router.js:173:109)\n at async Module.route (crates/next-dev-tests/tests/integration/turbopack/basic/comptime/input/.next/build/router/chunks/router.js:179:36)\n at async Module.run (crates/next-dev-tests/tests/integration/turbopack/basic/comptime/input/.next/build/router/chunks/[turbopack-node]_ipc_evaluate.ts._.js:142:39)\n", detail: "", documentation_link: "", source: None,