@@ -28,7 +28,11 @@ use turbopack_binding::{
2828 } ,
2929} ;
3030
31- use crate :: { next_config:: NextConfig , next_import_map:: get_next_package} ;
31+ use crate :: {
32+ next_config:: { NextConfig , RouteHas } ,
33+ next_import_map:: get_next_package,
34+ next_manifests:: MiddlewareMatcher ,
35+ } ;
3236
3337const NEXT_TEMPLATE_PATH : & str = "dist/esm/build/templates" ;
3438
@@ -151,13 +155,20 @@ impl NextRuntime {
151155 }
152156}
153157
158+ #[ turbo_tasks:: value]
159+ #[ derive( Debug , Clone ) ]
160+ pub enum MiddlewareMatcherKind {
161+ Str ( String ) ,
162+ Matcher ( MiddlewareMatcher ) ,
163+ }
164+
154165#[ turbo_tasks:: value]
155166#[ derive( Default , Clone ) ]
156167pub struct NextSourceConfig {
157168 pub runtime : NextRuntime ,
158169
159170 /// Middleware router matchers
160- pub matcher : Option < Vec < String > > ,
171+ pub matcher : Option < Vec < MiddlewareMatcherKind > > ,
161172}
162173
163174#[ turbo_tasks:: value_impl]
@@ -215,6 +226,139 @@ impl Issue for NextSourceConfigParsingIssue {
215226 }
216227}
217228
229+ fn emit_invalid_config_warning ( ident : Vc < AssetIdent > , detail : & str , value : & JsValue ) {
230+ let ( explainer, hints) = value. explain ( 2 , 0 ) ;
231+ NextSourceConfigParsingIssue {
232+ ident,
233+ detail : StyledString :: Text ( format ! ( "{detail} Got {explainer}.{hints}" ) ) . cell ( ) ,
234+ }
235+ . cell ( )
236+ . emit ( )
237+ }
238+
239+ fn parse_route_matcher_from_js_value (
240+ ident : Vc < AssetIdent > ,
241+ value : & JsValue ,
242+ ) -> Option < Vec < MiddlewareMatcherKind > > {
243+ let parse_matcher_kind_matcher = |value : & JsValue | {
244+ let mut route_has = vec ! [ ] ;
245+ if let JsValue :: Array { items, .. } = value {
246+ for item in items {
247+ if let JsValue :: Object { parts, .. } = item {
248+ let mut route_type = None ;
249+ let mut route_key = None ;
250+ let mut route_value = None ;
251+
252+ for matcher_part in parts {
253+ if let ObjectPart :: KeyValue ( part_key, part_value) = matcher_part {
254+ match part_key. as_str ( ) {
255+ Some ( "type" ) => {
256+ route_type = part_value. as_str ( ) . map ( |v| v. to_string ( ) )
257+ }
258+ Some ( "key" ) => {
259+ route_key = part_value. as_str ( ) . map ( |v| v. to_string ( ) )
260+ }
261+ Some ( "value" ) => {
262+ route_value = part_value. as_str ( ) . map ( |v| v. to_string ( ) )
263+ }
264+ _ => { }
265+ }
266+ }
267+ }
268+ let r = match route_type. as_deref ( ) {
269+ Some ( "header" ) => route_key. map ( |route_key| RouteHas :: Header {
270+ key : route_key,
271+ value : route_value,
272+ } ) ,
273+ Some ( "cookie" ) => route_key. map ( |route_key| RouteHas :: Cookie {
274+ key : route_key,
275+ value : route_value,
276+ } ) ,
277+ Some ( "query" ) => route_key. map ( |route_key| RouteHas :: Query {
278+ key : route_key,
279+ value : route_value,
280+ } ) ,
281+ Some ( "host" ) => {
282+ route_value. map ( |route_value| RouteHas :: Host { value : route_value } )
283+ }
284+ _ => None ,
285+ } ;
286+
287+ if let Some ( r) = r {
288+ route_has. push ( r) ;
289+ }
290+ }
291+ }
292+ }
293+
294+ route_has
295+ } ;
296+
297+ let mut matchers = vec ! [ ] ;
298+
299+ match value {
300+ JsValue :: Constant ( matcher) => {
301+ if let Some ( matcher) = matcher. as_str ( ) {
302+ matchers. push ( MiddlewareMatcherKind :: Str ( matcher. to_string ( ) ) ) ;
303+ } else {
304+ emit_invalid_config_warning (
305+ ident,
306+ "The matcher property must be a string or array of strings" ,
307+ value,
308+ ) ;
309+ }
310+ }
311+ JsValue :: Array { items, .. } => {
312+ for item in items {
313+ if let Some ( matcher) = item. as_str ( ) {
314+ matchers. push ( MiddlewareMatcherKind :: Str ( matcher. to_string ( ) ) ) ;
315+ } else if let JsValue :: Object { parts, .. } = item {
316+ let mut matcher = MiddlewareMatcher :: default ( ) ;
317+ for matcher_part in parts {
318+ if let ObjectPart :: KeyValue ( key, value) = matcher_part {
319+ match key. as_str ( ) {
320+ Some ( "source" ) => {
321+ if let Some ( value) = value. as_str ( ) {
322+ matcher. original_source = value. to_string ( ) ;
323+ }
324+ }
325+ Some ( "missing" ) => {
326+ matcher. missing = Some ( parse_matcher_kind_matcher ( value) )
327+ }
328+ Some ( "has" ) => {
329+ matcher. has = Some ( parse_matcher_kind_matcher ( value) )
330+ }
331+ _ => {
332+ //noop
333+ }
334+ }
335+ }
336+ }
337+
338+ matchers. push ( MiddlewareMatcherKind :: Matcher ( matcher) ) ;
339+ } else {
340+ emit_invalid_config_warning (
341+ ident,
342+ "The matcher property must be a string or array of strings" ,
343+ value,
344+ ) ;
345+ }
346+ }
347+ }
348+ _ => emit_invalid_config_warning (
349+ ident,
350+ "The matcher property must be a string or array of strings" ,
351+ value,
352+ ) ,
353+ }
354+
355+ if matchers. is_empty ( ) {
356+ None
357+ } else {
358+ Some ( matchers)
359+ }
360+ }
361+
218362#[ turbo_tasks:: function]
219363pub async fn parse_config_from_source ( module : Vc < Box < dyn Module > > ) -> Result < Vc < NextSourceConfig > > {
220364 if let Some ( ecmascript_asset) =
@@ -323,19 +467,12 @@ pub async fn parse_config_from_source(module: Vc<Box<dyn Module>>) -> Result<Vc<
323467
324468fn parse_config_from_js_value ( module : Vc < Box < dyn Module > > , value : & JsValue ) -> NextSourceConfig {
325469 let mut config = NextSourceConfig :: default ( ) ;
326- let invalid_config = |detail : & str , value : & JsValue | {
327- let ( explainer, hints) = value. explain ( 2 , 0 ) ;
328- NextSourceConfigParsingIssue {
329- ident : module. ident ( ) ,
330- detail : StyledString :: Text ( format ! ( "{detail} Got {explainer}.{hints}" ) ) . cell ( ) ,
331- }
332- . cell ( )
333- . emit ( )
334- } ;
470+
335471 if let JsValue :: Object { parts, .. } = value {
336472 for part in parts {
337473 match part {
338- ObjectPart :: Spread ( _) => invalid_config (
474+ ObjectPart :: Spread ( _) => emit_invalid_config_warning (
475+ module. ident ( ) ,
339476 "Spread properties are not supported in the config export." ,
340477 value,
341478 ) ,
@@ -352,7 +489,8 @@ fn parse_config_from_js_value(module: Vc<Box<dyn Module>>, value: &JsValue) -> N
352489 config. runtime = NextRuntime :: NodeJs ;
353490 }
354491 _ => {
355- invalid_config (
492+ emit_invalid_config_warning (
493+ module. ident ( ) ,
356494 "The runtime property must be either \" nodejs\" \
357495 or \" edge\" .",
358496 value,
@@ -361,48 +499,20 @@ fn parse_config_from_js_value(module: Vc<Box<dyn Module>>, value: &JsValue) -> N
361499 }
362500 }
363501 } else {
364- invalid_config (
502+ emit_invalid_config_warning (
503+ module. ident ( ) ,
365504 "The runtime property must be a constant string." ,
366505 value,
367506 ) ;
368507 }
369508 }
370509 if key == "matcher" {
371- let mut matchers = vec ! [ ] ;
372- match value {
373- JsValue :: Constant ( matcher) => {
374- if let Some ( matcher) = matcher. as_str ( ) {
375- matchers. push ( matcher. to_string ( ) ) ;
376- } else {
377- invalid_config (
378- "The matcher property must be a string or array of \
379- strings",
380- value,
381- ) ;
382- }
383- }
384- JsValue :: Array { items, .. } => {
385- for item in items {
386- if let Some ( matcher) = item. as_str ( ) {
387- matchers. push ( matcher. to_string ( ) ) ;
388- } else {
389- invalid_config (
390- "The matcher property must be a string or array \
391- of strings",
392- value,
393- ) ;
394- }
395- }
396- }
397- _ => invalid_config (
398- "The matcher property must be a string or array of strings" ,
399- value,
400- ) ,
401- }
402- config. matcher = Some ( matchers) ;
510+ config. matcher =
511+ parse_route_matcher_from_js_value ( module. ident ( ) , value) ;
403512 }
404513 } else {
405- invalid_config (
514+ emit_invalid_config_warning (
515+ module. ident ( ) ,
406516 "The exported config object must not contain non-constant strings." ,
407517 key,
408518 ) ;
@@ -411,7 +521,8 @@ fn parse_config_from_js_value(module: Vc<Box<dyn Module>>, value: &JsValue) -> N
411521 }
412522 }
413523 } else {
414- invalid_config (
524+ emit_invalid_config_warning (
525+ module. ident ( ) ,
415526 "The exported config object must be a valid object literal." ,
416527 value,
417528 ) ;
0 commit comments