Skip to content

Commit 074262b

Browse files
mischnicdevjiwonchoiijjk
authored
Only allow node runtime in proxy (#85139)
This PR is the initiative of deprecating the edge runtime. Proxy will default to Node.js runtime, and will not allow runtime config. If the users want to use the edge runtime, they should stay on the Middleware, though we recommend starting the migration. --------- Co-authored-by: devjiwonchoi <[email protected]> Co-authored-by: JJ Kasper <[email protected]>
1 parent 47ceda3 commit 074262b

File tree

31 files changed

+347
-165
lines changed

31 files changed

+347
-165
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/next-api/src/middleware.rs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use next_core::{
66
middleware::get_middleware_module,
77
next_edge::entry::wrap_edge_entry,
88
next_manifests::{EdgeFunctionDefinition, MiddlewaresManifestV2, ProxyMatcher, Regions},
9-
parse_segment_config_from_source,
10-
segment_config::ParseSegmentMode,
9+
segment_config::NextSegmentConfig,
1110
util::{MiddlewareMatcherKind, NextRuntime},
1211
};
1312
use tracing::Instrument;
@@ -49,6 +48,8 @@ pub struct MiddlewareEndpoint {
4948
source: ResolvedVc<Box<dyn Source>>,
5049
app_dir: Option<FileSystemPath>,
5150
ecmascript_client_reference_transition_name: Option<RcStr>,
51+
config: ResolvedVc<NextSegmentConfig>,
52+
runtime: NextRuntime,
5253
}
5354

5455
#[turbo_tasks::value_impl]
@@ -60,13 +61,17 @@ impl MiddlewareEndpoint {
6061
source: ResolvedVc<Box<dyn Source>>,
6162
app_dir: Option<FileSystemPath>,
6263
ecmascript_client_reference_transition_name: Option<RcStr>,
64+
config: ResolvedVc<NextSegmentConfig>,
65+
runtime: NextRuntime,
6366
) -> Vc<Self> {
6467
Self {
6568
project,
6669
asset_context,
6770
source,
6871
app_dir,
6972
ecmascript_client_reference_transition_name,
73+
config,
74+
runtime,
7075
}
7176
.cell()
7277
}
@@ -81,20 +86,20 @@ impl MiddlewareEndpoint {
8186
)
8287
.module();
8388

89+
let userland_path = userland_module.ident().path().await?;
90+
let is_proxy = userland_path.file_stem() == Some("proxy");
91+
8492
let module = get_middleware_module(
8593
*self.asset_context,
8694
self.project.project_path().owned().await?,
8795
userland_module,
96+
is_proxy,
8897
);
8998

90-
let runtime = parse_segment_config_from_source(*self.source, ParseSegmentMode::Base)
91-
.await?
92-
.runtime
93-
.unwrap_or(NextRuntime::Edge);
94-
95-
if matches!(runtime, NextRuntime::NodeJs) {
99+
if matches!(self.runtime, NextRuntime::NodeJs) {
96100
return Ok(module);
97101
}
102+
98103
Ok(wrap_edge_entry(
99104
*self.asset_context,
100105
self.project.project_path().owned().await?,
@@ -152,10 +157,7 @@ impl MiddlewareEndpoint {
152157
#[turbo_tasks::function]
153158
async fn output_assets(self: Vc<Self>) -> Result<Vc<OutputAssets>> {
154159
let this = self.await?;
155-
156-
let config =
157-
parse_segment_config_from_source(*self.await?.source, ParseSegmentMode::Base).await?;
158-
let runtime = config.runtime.unwrap_or(NextRuntime::Edge);
160+
let config = this.config.await?;
159161

160162
let next_config = this.project.next_config();
161163
let i18n = next_config.i18n().await?;
@@ -223,7 +225,7 @@ impl MiddlewareEndpoint {
223225
}]
224226
};
225227

226-
if matches!(runtime, NextRuntime::NodeJs) {
228+
if matches!(this.runtime, NextRuntime::NodeJs) {
227229
let chunk = self.node_chunk().to_resolved().await?;
228230
let mut output_assets = vec![chunk];
229231
if this.project.next_mode().await?.is_production() {

crates/next-api/src/project.rs

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,28 +1437,6 @@ impl Project {
14371437
)))
14381438
}
14391439

1440-
#[turbo_tasks::function]
1441-
async fn middleware_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
1442-
let edge_module_context = self.edge_middleware_context();
1443-
1444-
let middleware = self.find_middleware();
1445-
let FindContextFileResult::Found(fs_path, _) = &*middleware.await? else {
1446-
return Ok(edge_module_context);
1447-
};
1448-
let source = Vc::upcast(FileSource::new(fs_path.clone()));
1449-
1450-
let runtime = parse_segment_config_from_source(source, ParseSegmentMode::Base)
1451-
.await?
1452-
.runtime
1453-
.unwrap_or(NextRuntime::Edge);
1454-
1455-
if matches!(runtime, NextRuntime::NodeJs) {
1456-
Ok(self.node_middleware_context())
1457-
} else {
1458-
Ok(edge_module_context)
1459-
}
1460-
}
1461-
14621440
#[turbo_tasks::function]
14631441
async fn find_middleware(self: Vc<Self>) -> Result<Vc<FindContextFileResult>> {
14641442
Ok(find_context_file(
@@ -1483,14 +1461,34 @@ impl Project {
14831461
.as_ref()
14841462
.map(|_| AppProject::client_transition_name());
14851463

1486-
let middleware_asset_context = self.middleware_context();
1464+
let is_proxy = fs_path.file_stem() == Some("proxy");
1465+
let config = parse_segment_config_from_source(
1466+
source,
1467+
if is_proxy {
1468+
ParseSegmentMode::Proxy
1469+
} else {
1470+
ParseSegmentMode::Base
1471+
},
1472+
);
1473+
let runtime = config.await?.runtime.unwrap_or(if is_proxy {
1474+
NextRuntime::NodeJs
1475+
} else {
1476+
NextRuntime::Edge
1477+
});
1478+
1479+
let middleware_asset_context = match runtime {
1480+
NextRuntime::NodeJs => self.node_middleware_context(),
1481+
NextRuntime::Edge => self.edge_middleware_context(),
1482+
};
14871483

14881484
Ok(Vc::upcast(MiddlewareEndpoint::new(
14891485
self,
14901486
middleware_asset_context,
14911487
source,
14921488
app_dir.clone(),
14931489
ecmascript_client_reference_transition_name,
1490+
config,
1491+
runtime,
14941492
)))
14951493
}
14961494

crates/next-core/src/middleware.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ pub async fn get_middleware_module(
3232
asset_context: Vc<Box<dyn AssetContext>>,
3333
project_root: FileSystemPath,
3434
userland_module: ResolvedVc<Box<dyn Module>>,
35+
is_proxy: bool,
3536
) -> Result<Vc<Box<dyn Module>>> {
3637
const INNER: &str = "INNER_MIDDLEWARE_MODULE";
3738

3839
// Determine if this is a proxy file by checking the module path
3940
let userland_path = userland_module.ident().path().await?;
40-
let is_proxy = userland_path.file_stem() == Some("proxy");
4141
let (file_type, function_name, page_path) = if is_proxy {
4242
("Proxy", "proxy", "/proxy")
4343
} else {
@@ -91,11 +91,7 @@ pub async fn get_middleware_module(
9191
let source = load_next_js_template(
9292
"middleware.js",
9393
project_root,
94-
&[
95-
("VAR_USERLAND", INNER),
96-
("VAR_DEFINITION_PAGE", page_path),
97-
("VAR_MODULE_RELATIVE_PATH", userland_path.path.as_str()),
98-
],
94+
&[("VAR_USERLAND", INNER), ("VAR_DEFINITION_PAGE", page_path)],
9995
&[],
10096
&[],
10197
)

crates/next-core/src/segment_config.rs

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ pub enum ParseSegmentMode {
289289
Base,
290290
// Disallows "use client + generateStatic" and ignores/warns about `export const config`
291291
App,
292+
// Disallows config = { runtime: "edge" }
293+
Proxy,
292294
}
293295

294296
/// Parse the raw source code of a file to get the segment config local to that file.
@@ -667,21 +669,35 @@ async fn parse_config_value(
667669
.await;
668670
};
669671

670-
config.runtime =
671-
match serde_json::from_value(Value::String(val.to_string())) {
672-
Ok(runtime) => Some(runtime),
673-
Err(err) => {
674-
return invalid_config(
675-
source,
676-
"config",
677-
span,
678-
format!("`runtime` has an invalid value: {err}.").into(),
679-
Some(value),
680-
IssueSeverity::Error,
681-
)
682-
.await;
683-
}
684-
};
672+
let runtime = match serde_json::from_value(Value::String(val.to_string())) {
673+
Ok(runtime) => Some(runtime),
674+
Err(err) => {
675+
return invalid_config(
676+
source,
677+
"config",
678+
span,
679+
format!("`runtime` has an invalid value: {err}.").into(),
680+
Some(value),
681+
IssueSeverity::Error,
682+
)
683+
.await;
684+
}
685+
};
686+
687+
if mode == ParseSegmentMode::Proxy && runtime == Some(NextRuntime::Edge) {
688+
invalid_config(
689+
source,
690+
"config",
691+
span,
692+
rcstr!("Proxy does not support Edge runtime."),
693+
Some(value),
694+
IssueSeverity::Error,
695+
)
696+
.await?;
697+
continue;
698+
}
699+
700+
config.runtime = runtime
685701
}
686702
"matcher" => {
687703
config.middleware_matcher =

packages/next/errors.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -901,5 +901,6 @@
901901
"900": "Both %s file \"./%s\" and %s file \"./%s\" are detected. Please use \"./%s\" only. Learn more: https://nextjs.org/docs/messages/middleware-to-proxy",
902902
"901": "Invalid \"cacheHandlers\" provided, expected an object e.g. { default: '/my-handler.js' }, received %s",
903903
"902": "Invalid handler fields configured for \"cacheHandlers\":\\n%s",
904-
"903": "The file \"%s\" must export a function, either as a default export or as a named \"%s\" export.\\nThis function is what Next.js runs for every request handled by this %s.\\n\\nWhy this happens:\\n%s- The file exists but doesn't export a function.\\n- The export is not a function (e.g., an object or constant).\\n- There's a syntax error preventing the export from being recognized.\\n\\nTo fix it:\\n- Ensure this file has either a default or \"%s\" function export.\\n\\nLearn more: https://nextjs.org/docs/messages/middleware-to-proxy"
904+
"903": "The file \"%s\" must export a function, either as a default export or as a named \"%s\" export.\\nThis function is what Next.js runs for every request handled by this %s.\\n\\nWhy this happens:\\n%s- The file exists but doesn't export a function.\\n- The export is not a function (e.g., an object or constant).\\n- There's a syntax error preventing the export from being recognized.\\n\\nTo fix it:\\n- Ensure this file has either a default or \"%s\" function export.\\n\\nLearn more: https://nextjs.org/docs/messages/middleware-to-proxy",
905+
"904": "The file \"%s\" must export a function, either as a default export or as a named \"%s\" export."
905906
}

0 commit comments

Comments
 (0)