fix(next-core): allow runtime segment option in pages/api (#58409)

### What

This PR fixes turbopack to allow runtime segment option (https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes#segment-runtime-Option) in pages/api. 

Previously it only allows config object (https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher), so the single runtime export like `export const runtime = 'edge'` didn't work. PR updates logic to parse config in the module to allow single segment export as well.

It doesn't allow _both_, if config object is exported it'll short-curcuit to read values as config object should able to specify runtime without separate runtime segment.

Closes PACK-1961
This commit is contained in:
OJ Kwon 2023-11-17 12:59:01 -08:00 committed by GitHub
parent 68e74b44bc
commit 1f3178e743
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 9 deletions

View file

@ -2,7 +2,7 @@ use anyhow::{bail, Context, Result};
use indexmap::{IndexMap, IndexSet};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::Value as JsonValue;
use swc_core::ecma::ast::Program;
use swc_core::ecma::ast::{Expr, Lit, Program};
use turbo_tasks::{trace::TraceRawVcs, TaskInput, ValueDefault, ValueToString, Vc};
use turbo_tasks_fs::{rope::Rope, util::join_path, File};
use turbopack_binding::{
@ -224,9 +224,11 @@ pub async fn parse_config_from_source(module: Vc<Box<dyn Module>>) -> Result<Vc<
.and_then(|export_decl| export_decl.decl.as_var())
{
for decl in &decl.decls {
if decl
.name
.as_ident()
let decl_ident = decl.name.as_ident();
// Check if there is exported config object `export const config = {...}`
// https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
if decl_ident
.map(|ident| &*ident.sym == "config")
.unwrap_or_default()
{
@ -246,6 +248,56 @@ pub async fn parse_config_from_source(module: Vc<Box<dyn Module>>) -> Result<Vc<
.emit()
}
}
// Or, check if there is segment runtime option
// https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes#segment-runtime-Option
else if decl_ident
.map(|ident| &*ident.sym == "runtime")
.unwrap_or_default()
{
let runtime_value_issue = NextSourceConfigParsingIssue {
ident: module.ident(),
detail: Vc::cell(
"The runtime property must be either \"nodejs\" or \"edge\"."
.to_string(),
),
}
.cell();
if let Some(init) = decl.init.as_ref() {
// skipping eval and directly read the expr's value, as we know it
// should be a const string
if let Expr::Lit(Lit::Str(str_value)) = &**init {
let mut config = NextSourceConfig::default();
let runtime = str_value.value.to_string();
match runtime.as_str() {
"edge" | "experimental-edge" => {
config.runtime = NextRuntime::Edge;
}
"nodejs" => {
config.runtime = NextRuntime::NodeJs;
}
_ => {
runtime_value_issue.emit();
}
}
return Ok(config.cell());
} else {
runtime_value_issue.emit();
}
} else {
NextSourceConfigParsingIssue {
ident: module.ident(),
detail: Vc::cell(
"The exported segment runtime option must contain an \
variable initializer."
.to_string(),
),
}
.cell()
.emit()
}
}
}
}
}

View file

@ -2454,6 +2454,7 @@
"test/e2e/app-dir/app-middleware/app-middleware.test.ts": {
"passed": [
"app dir - middleware without pages dir Updates headers",
"app dir - middleware with middleware in src dir works without crashing when using requestAsyncStorage",
"app-dir with middleware Mutate request headers for Edge Functions Supports draft mode",
"app-dir with middleware Mutate request headers for Serverless Functions Adds new headers",
"app-dir with middleware Mutate request headers for Serverless Functions Deletes headers",
@ -2463,14 +2464,12 @@
"app-dir with middleware Mutate request headers for next/headers Deletes headers",
"app-dir with middleware Mutate request headers for next/headers Supports draft mode",
"app-dir with middleware Mutate request headers for next/headers Updates headers",
"app-dir with middleware should filter correctly after middleware rewrite"
],
"failed": [
"app dir - middleware with middleware in src dir works without crashing when using requestAsyncStorage",
"app-dir with middleware Mutate request headers for Edge Functions Adds new headers",
"app-dir with middleware Mutate request headers for Edge Functions Deletes headers",
"app-dir with middleware Mutate request headers for Edge Functions Updates headers"
"app-dir with middleware Mutate request headers for Edge Functions Updates headers",
"app-dir with middleware should filter correctly after middleware rewrite"
],
"failed": [],
"pending": [],
"flakey": [],
"runtimeError": false