Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 15 additions & 13 deletions crates/next-api/src/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ use next_core::{
middleware::get_middleware_module,
next_edge::entry::wrap_edge_entry,
next_manifests::{EdgeFunctionDefinition, MiddlewaresManifestV2, ProxyMatcher, Regions},
parse_segment_config_from_source,
segment_config::ParseSegmentMode,
segment_config::NextSegmentConfig,
util::{MiddlewareMatcherKind, NextRuntime},
};
use tracing::Instrument;
Expand Down Expand Up @@ -49,6 +48,8 @@ pub struct MiddlewareEndpoint {
source: ResolvedVc<Box<dyn Source>>,
app_dir: Option<FileSystemPath>,
ecmascript_client_reference_transition_name: Option<RcStr>,
config: ResolvedVc<NextSegmentConfig>,
runtime: NextRuntime,
}

#[turbo_tasks::value_impl]
Expand All @@ -60,13 +61,17 @@ impl MiddlewareEndpoint {
source: ResolvedVc<Box<dyn Source>>,
app_dir: Option<FileSystemPath>,
ecmascript_client_reference_transition_name: Option<RcStr>,
config: ResolvedVc<NextSegmentConfig>,
runtime: NextRuntime,
) -> Vc<Self> {
Self {
project,
asset_context,
source,
app_dir,
ecmascript_client_reference_transition_name,
config,
runtime,
}
.cell()
}
Expand All @@ -81,20 +86,20 @@ impl MiddlewareEndpoint {
)
.module();

let userland_path = userland_module.ident().path().await?;
let is_proxy = userland_path.file_stem() == Some("proxy");

let module = get_middleware_module(
*self.asset_context,
self.project.project_path().owned().await?,
userland_module,
is_proxy,
);

let runtime = parse_segment_config_from_source(*self.source, ParseSegmentMode::Base)
.await?
.runtime
.unwrap_or(NextRuntime::Edge);

if matches!(runtime, NextRuntime::NodeJs) {
if matches!(self.runtime, NextRuntime::NodeJs) {
return Ok(module);
}

Ok(wrap_edge_entry(
*self.asset_context,
self.project.project_path().owned().await?,
Expand Down Expand Up @@ -152,10 +157,7 @@ impl MiddlewareEndpoint {
#[turbo_tasks::function]
async fn output_assets(self: Vc<Self>) -> Result<Vc<OutputAssets>> {
let this = self.await?;

let config =
parse_segment_config_from_source(*self.await?.source, ParseSegmentMode::Base).await?;
let runtime = config.runtime.unwrap_or(NextRuntime::Edge);
let config = this.config.await?;

let next_config = this.project.next_config();
let i18n = next_config.i18n().await?;
Expand Down Expand Up @@ -223,7 +225,7 @@ impl MiddlewareEndpoint {
}]
};

if matches!(runtime, NextRuntime::NodeJs) {
if matches!(this.runtime, NextRuntime::NodeJs) {
let chunk = self.node_chunk().to_resolved().await?;
let mut output_assets = vec![chunk];
if this.project.next_mode().await?.is_production() {
Expand Down
44 changes: 21 additions & 23 deletions crates/next-api/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@
/// E.g. `/home/user/projects/my-repo`.
pub root_path: RcStr,

/// A path which contains the app/pages directories, relative to [`Project::root_path`], always

Check warning on line 156 in crates/next-api/src/project.rs

View workflow job for this annotation

GitHub Actions / rustdoc check / build

public documentation for `project_path` links to private item `Project::root_path`
/// Unix path. E.g. `apps/my-app`
pub project_path: RcStr,

Expand Down Expand Up @@ -1437,28 +1437,6 @@
)))
}

#[turbo_tasks::function]
async fn middleware_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
let edge_module_context = self.edge_middleware_context();

let middleware = self.find_middleware();
let FindContextFileResult::Found(fs_path, _) = &*middleware.await? else {
return Ok(edge_module_context);
};
let source = Vc::upcast(FileSource::new(fs_path.clone()));

let runtime = parse_segment_config_from_source(source, ParseSegmentMode::Base)
.await?
.runtime
.unwrap_or(NextRuntime::Edge);

if matches!(runtime, NextRuntime::NodeJs) {
Ok(self.node_middleware_context())
} else {
Ok(edge_module_context)
}
}

#[turbo_tasks::function]
async fn find_middleware(self: Vc<Self>) -> Result<Vc<FindContextFileResult>> {
Ok(find_context_file(
Expand All @@ -1483,14 +1461,34 @@
.as_ref()
.map(|_| AppProject::client_transition_name());

let middleware_asset_context = self.middleware_context();
let is_proxy = fs_path.file_stem() == Some("proxy");
let config = parse_segment_config_from_source(
source,
if is_proxy {
ParseSegmentMode::Proxy
} else {
ParseSegmentMode::Base
},
);
let runtime = config.await?.runtime.unwrap_or(if is_proxy {
NextRuntime::NodeJs
} else {
NextRuntime::Edge
});

let middleware_asset_context = match runtime {
NextRuntime::NodeJs => self.node_middleware_context(),
NextRuntime::Edge => self.edge_middleware_context(),
};

Ok(Vc::upcast(MiddlewareEndpoint::new(
self,
middleware_asset_context,
source,
app_dir.clone(),
ecmascript_client_reference_transition_name,
config,
runtime,
)))
}

Expand Down
8 changes: 2 additions & 6 deletions crates/next-core/src/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ pub async fn get_middleware_module(
asset_context: Vc<Box<dyn AssetContext>>,
project_root: FileSystemPath,
userland_module: ResolvedVc<Box<dyn Module>>,
is_proxy: bool,
) -> Result<Vc<Box<dyn Module>>> {
const INNER: &str = "INNER_MIDDLEWARE_MODULE";

// Determine if this is a proxy file by checking the module path
let userland_path = userland_module.ident().path().await?;
let is_proxy = userland_path.file_stem() == Some("proxy");
let (file_type, function_name, page_path) = if is_proxy {
("Proxy", "proxy", "/proxy")
} else {
Expand Down Expand Up @@ -91,11 +91,7 @@ pub async fn get_middleware_module(
let source = load_next_js_template(
"middleware.js",
project_root,
&[
("VAR_USERLAND", INNER),
("VAR_DEFINITION_PAGE", page_path),
("VAR_MODULE_RELATIVE_PATH", userland_path.path.as_str()),
],
&[("VAR_USERLAND", INNER), ("VAR_DEFINITION_PAGE", page_path)],
&[],
&[],
)
Expand Down
46 changes: 31 additions & 15 deletions crates/next-core/src/segment_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,8 @@ pub enum ParseSegmentMode {
Base,
// Disallows "use client + generateStatic" and ignores/warns about `export const config`
App,
// Disallows config = { runtime: "edge" }
Proxy,
}

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

config.runtime =
match serde_json::from_value(Value::String(val.to_string())) {
Ok(runtime) => Some(runtime),
Err(err) => {
return invalid_config(
source,
"config",
span,
format!("`runtime` has an invalid value: {err}.").into(),
Some(value),
IssueSeverity::Error,
)
.await;
}
};
let runtime = match serde_json::from_value(Value::String(val.to_string())) {
Ok(runtime) => Some(runtime),
Err(err) => {
return invalid_config(
source,
"config",
span,
format!("`runtime` has an invalid value: {err}.").into(),
Some(value),
IssueSeverity::Error,
)
.await;
}
};

if mode == ParseSegmentMode::Proxy && runtime == Some(NextRuntime::Edge) {
invalid_config(
source,
"config",
span,
rcstr!("Proxy does not support Edge runtime."),
Some(value),
IssueSeverity::Error,
)
.await?;
continue;
}

config.runtime = runtime
}
"matcher" => {
config.middleware_matcher =
Expand Down
3 changes: 2 additions & 1 deletion packages/next/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -901,5 +901,6 @@
"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",
"901": "Invalid \"cacheHandlers\" provided, expected an object e.g. { default: '/my-handler.js' }, received %s",
"902": "Invalid handler fields configured for \"cacheHandlers\":\\n%s",
"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"
"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": "The file \"%s\" must export a function, either as a default export or as a named \"%s\" export."
}
Loading
Loading