@@ -19,6 +19,7 @@ import { isEdgeRuntime } from '../../lib/is-edge-runtime'
19
19
import { RSC_MODULE_TYPES } from '../../shared/lib/constants'
20
20
import type { RSCMeta } from '../webpack/loaders/get-module-build-info'
21
21
import { PAGE_TYPES } from '../../lib/page-types'
22
+ import { AppSegmentConfigSchemaKeys } from '../app-segment-config'
22
23
23
24
// TODO: migrate preferredRegion here
24
25
// Don't forget to update the next-types-plugin file as well
@@ -503,180 +504,202 @@ export async function getPageStaticInfo(params: {
503
504
} ) : Promise < PageStaticInfo > {
504
505
const { isDev, pageFilePath, nextConfig, page, pageType } = params
505
506
506
- const fileContent = ( await tryToReadFile ( pageFilePath , ! isDev ) ) || ''
507
+ const fileContent = await tryToReadFile ( pageFilePath , ! isDev )
508
+
509
+ // If there's no file content or the file doesn't contain any of the keywords
510
+ // that we expect to find, then we should just bail the detection now.
507
511
if (
508
- / (?< ! ( _ j s x | j s x - ) ) r u n t i m e | p r e f e r r e d R e g i o n | g e t S t a t i c P r o p s | g e t S e r v e r S i d e P r o p s | g e n e r a t e S t a t i c P a r a m s | e x p o r t c o n s t | g e n e r a t e I m a g e M e t a d a t a | g e n e r a t e S i t e m a p s / . test (
512
+ ! fileContent ||
513
+ ! / (?< ! ( _ j s x | j s x - ) ) r u n t i m e | p r e f e r r e d R e g i o n | g e t S t a t i c P r o p s | g e t S e r v e r S i d e P r o p s | g e n e r a t e S t a t i c P a r a m s | e x p o r t c o n s t | g e n e r a t e I m a g e M e t a d a t a | g e n e r a t e S i t e m a p s / . test (
509
514
fileContent
510
515
)
511
516
) {
512
- const swcAST = await parseModule ( pageFilePath , fileContent )
513
- const {
514
- ssg,
515
- ssr,
516
- runtime,
517
- preferredRegion,
518
- generateStaticParams,
519
- generateImageMetadata,
520
- generateSitemaps,
521
- extraProperties,
522
- directives,
523
- } = checkExports ( swcAST , pageFilePath )
524
- const rscInfo = getRSCModuleInformation ( fileContent , true )
525
- const rsc = rscInfo . type
526
-
527
- // default / failsafe value for config
528
- let config : any // TODO: type this as unknown
529
- try {
530
- config = extractExportedConstValue ( swcAST , 'config' )
531
- } catch ( e ) {
532
- if ( e instanceof UnsupportedValueError ) {
533
- warnAboutUnsupportedValue ( pageFilePath , page , e )
534
- }
535
- // `export config` doesn't exist, or other unknown error thrown by swc, silence them
517
+ return {
518
+ ssr : false ,
519
+ ssg : false ,
520
+ rsc : RSC_MODULE_TYPES . server ,
521
+ generateStaticParams : false ,
522
+ generateImageMetadata : false ,
523
+ generateSitemaps : false ,
524
+ amp : false ,
525
+ runtime : undefined ,
536
526
}
527
+ }
537
528
538
- const extraConfig : Record < string , any > = { }
539
-
540
- if ( extraProperties && pageType === PAGE_TYPES . APP ) {
541
- for ( const prop of extraProperties ) {
542
- if ( ! AUTHORIZED_EXTRA_ROUTER_PROPS . includes ( prop ) ) continue
543
- try {
544
- extraConfig [ prop ] = extractExportedConstValue ( swcAST , prop )
545
- } catch ( e ) {
546
- if ( e instanceof UnsupportedValueError ) {
547
- warnAboutUnsupportedValue ( pageFilePath , page , e )
548
- }
549
- }
550
- }
551
- } else if ( pageType === PAGE_TYPES . PAGES ) {
552
- for ( const key in config ) {
553
- if ( ! AUTHORIZED_EXTRA_ROUTER_PROPS . includes ( key ) ) continue
554
- extraConfig [ key ] = config [ key ]
555
- }
529
+ const swcAST = await parseModule ( pageFilePath , fileContent )
530
+
531
+ const {
532
+ ssg,
533
+ ssr,
534
+ runtime,
535
+ preferredRegion,
536
+ generateStaticParams,
537
+ generateImageMetadata,
538
+ generateSitemaps,
539
+ extraProperties,
540
+ directives,
541
+ } = checkExports ( swcAST , pageFilePath )
542
+
543
+ const rscInfo = getRSCModuleInformation ( fileContent , true )
544
+ const rsc = rscInfo . type
545
+
546
+ // default / failsafe value for config
547
+ let config : any // TODO: type this as unknown
548
+ try {
549
+ config = extractExportedConstValue ( swcAST , 'config' )
550
+ } catch ( e ) {
551
+ if ( e instanceof UnsupportedValueError ) {
552
+ warnAboutUnsupportedValue ( pageFilePath , page , e )
556
553
}
554
+ // `export config` doesn't exist, or other unknown error thrown by swc, silence them
555
+ }
557
556
558
- if ( pageType === PAGE_TYPES . APP ) {
559
- if ( config ) {
560
- let message = `Page config in ${ pageFilePath } is deprecated. Replace \`export const config=…\` with the following:`
557
+ const extraConfig : Record < string , any > = { }
561
558
562
- if ( config . runtime ) {
563
- message += `\n - \`export const runtime = ${ JSON . stringify (
564
- config . runtime
565
- ) } \``
559
+ if ( extraProperties && pageType === PAGE_TYPES . APP ) {
560
+ for ( const prop of extraProperties ) {
561
+ if ( ! AUTHORIZED_EXTRA_ROUTER_PROPS . includes ( prop ) ) continue
562
+ try {
563
+ extraConfig [ prop ] = extractExportedConstValue ( swcAST , prop )
564
+ } catch ( e ) {
565
+ if ( e instanceof UnsupportedValueError ) {
566
+ warnAboutUnsupportedValue ( pageFilePath , page , e )
566
567
}
568
+ }
569
+ }
570
+ } else if ( pageType === PAGE_TYPES . PAGES ) {
571
+ for ( const key in config ) {
572
+ if ( ! AUTHORIZED_EXTRA_ROUTER_PROPS . includes ( key ) ) continue
573
+ extraConfig [ key ] = config [ key ]
574
+ }
575
+ }
567
576
568
- if ( config . regions ) {
569
- message += `\n - \`export const preferredRegion = ${ JSON . stringify (
570
- config . regions
571
- ) } \``
572
- }
577
+ if ( pageType === PAGE_TYPES . APP ) {
578
+ if ( config ) {
579
+ let message = `Page config in ${ pageFilePath } is deprecated. Replace \`export const config=…\` with the following:`
573
580
574
- message += `\nVisit https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config for more information.`
581
+ if ( config . runtime ) {
582
+ message += `\n - \`export const runtime = ${ JSON . stringify (
583
+ config . runtime
584
+ ) } \``
585
+ }
575
586
576
- if ( isDev ) {
577
- Log . warnOnce ( message )
578
- } else {
579
- throw new Error ( message )
580
- }
581
- config = { }
587
+ if ( config . regions ) {
588
+ message += `\n - \`export const preferredRegion = ${ JSON . stringify (
589
+ config . regions
590
+ ) } \``
582
591
}
583
- }
584
- if ( ! config ) config = { }
585
-
586
- // We use `export const config = { runtime: '...' }` to specify the page runtime for pages/.
587
- // In the new app directory, we prefer to use `export const runtime = '...'`
588
- // and deprecate the old way. To prevent breaking changes for `pages`, we use the exported config
589
- // as the fallback value.
590
- let resolvedRuntime
591
- if ( pageType === PAGE_TYPES . APP ) {
592
- resolvedRuntime = runtime
593
- } else {
594
- resolvedRuntime = runtime || config . runtime
595
- }
596
592
597
- if (
598
- typeof resolvedRuntime !== 'undefined' &&
599
- resolvedRuntime !== SERVER_RUNTIME . nodejs &&
600
- ! isEdgeRuntime ( resolvedRuntime )
601
- ) {
602
- const options = Object . values ( SERVER_RUNTIME ) . join ( ', ' )
603
- const message =
604
- typeof resolvedRuntime !== 'string'
605
- ? `The \`runtime\` config must be a string. Please leave it empty or choose one of: ${ options } `
606
- : `Provided runtime "${ resolvedRuntime } " is not supported. Please leave it empty or choose one of: ${ options } `
593
+ message += `\nVisit https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config for more information.`
594
+
607
595
if ( isDev ) {
608
- Log . error ( message )
596
+ Log . warnOnce ( message )
609
597
} else {
610
598
throw new Error ( message )
611
599
}
600
+ config = { }
601
+ }
602
+ }
603
+ if ( ! config ) config = { }
604
+
605
+ // We use `export const config = { runtime: '...' }` to specify the page runtime for pages/.
606
+ // In the new app directory, we prefer to use `export const runtime = '...'`
607
+ // and deprecate the old way. To prevent breaking changes for `pages`, we use the exported config
608
+ // as the fallback value.
609
+ let resolvedRuntime
610
+ if ( pageType === PAGE_TYPES . APP ) {
611
+ resolvedRuntime = runtime
612
+ } else {
613
+ resolvedRuntime = runtime || config . runtime
614
+ }
615
+
616
+ if (
617
+ typeof resolvedRuntime !== 'undefined' &&
618
+ resolvedRuntime !== SERVER_RUNTIME . nodejs &&
619
+ ! isEdgeRuntime ( resolvedRuntime )
620
+ ) {
621
+ const options = Object . values ( SERVER_RUNTIME ) . join ( ', ' )
622
+ const message =
623
+ typeof resolvedRuntime !== 'string'
624
+ ? `The \`runtime\` config must be a string. Please leave it empty or choose one of: ${ options } `
625
+ : `Provided runtime "${ resolvedRuntime } " is not supported. Please leave it empty or choose one of: ${ options } `
626
+ if ( isDev ) {
627
+ Log . error ( message )
628
+ } else {
629
+ throw new Error ( message )
612
630
}
631
+ }
613
632
614
- const requiresServerRuntime = ssr || ssg || pageType === PAGE_TYPES . APP
633
+ const requiresServerRuntime = ssr || ssg || pageType === PAGE_TYPES . APP
615
634
616
- const isAnAPIRoute = isAPIRoute ( page ?. replace ( / ^ (?: \/ s r c ) ? \/ p a g e s \/ / , '/' ) )
635
+ const isAnAPIRoute =
636
+ pageType === PAGE_TYPES . PAGES &&
637
+ isAPIRoute ( page ?. replace ( / ^ (?: \/ s r c ) ? \/ p a g e s \/ / , '/' ) )
617
638
618
- resolvedRuntime =
619
- isEdgeRuntime ( resolvedRuntime ) || requiresServerRuntime
620
- ? resolvedRuntime
621
- : undefined
639
+ resolvedRuntime =
640
+ isEdgeRuntime ( resolvedRuntime ) || requiresServerRuntime
641
+ ? resolvedRuntime
642
+ : undefined
622
643
623
- if ( resolvedRuntime === SERVER_RUNTIME . experimentalEdge ) {
624
- warnAboutExperimentalEdge ( isAnAPIRoute ? page ! : null )
625
- }
644
+ if ( resolvedRuntime === SERVER_RUNTIME . experimentalEdge ) {
645
+ warnAboutExperimentalEdge ( isAnAPIRoute ? page ! : null )
646
+ }
626
647
627
- if (
628
- resolvedRuntime === SERVER_RUNTIME . edge &&
629
- pageType === PAGE_TYPES . PAGES &&
630
- page &&
631
- ! isAnAPIRoute
632
- ) {
633
- const message = `Page ${ page } provided runtime 'edge', the edge runtime for rendering is currently experimental. Use runtime 'experimental-edge' instead.`
634
- if ( isDev ) {
635
- Log . error ( message )
636
- } else {
637
- throw new Error ( message )
638
- }
648
+ if (
649
+ resolvedRuntime === SERVER_RUNTIME . edge &&
650
+ pageType === PAGE_TYPES . PAGES &&
651
+ page &&
652
+ ! isAnAPIRoute
653
+ ) {
654
+ const message = `Page ${ page } provided runtime 'edge', the edge runtime for rendering is currently experimental. Use runtime 'experimental-edge' instead.`
655
+ if ( isDev ) {
656
+ Log . error ( message )
657
+ } else {
658
+ throw new Error ( message )
639
659
}
660
+ }
640
661
641
- const middlewareConfig = getMiddlewareConfig (
642
- page ?? 'middleware/edge API route' ,
643
- config ,
644
- nextConfig
645
- )
662
+ const middlewareConfig = getMiddlewareConfig (
663
+ page ?? 'middleware/edge API route' ,
664
+ config ,
665
+ nextConfig
666
+ )
646
667
647
- if (
648
- pageType === PAGE_TYPES . APP &&
649
- directives ?. has ( 'client' ) &&
650
- generateStaticParams
651
- ) {
652
- throw new Error (
653
- `Page "${ page } " cannot use both "use client" and export function "generateStaticParams()".`
654
- )
655
- }
668
+ const isClientComponent = directives ? directives . has ( 'client' ) : false
656
669
657
- return {
658
- ssr,
659
- ssg,
660
- rsc,
661
- generateStaticParams,
662
- generateImageMetadata,
663
- generateSitemaps,
664
- amp : config . amp || false ,
665
- ...( middlewareConfig && { middleware : middlewareConfig } ) ,
666
- ...( resolvedRuntime && { runtime : resolvedRuntime } ) ,
667
- preferredRegion,
668
- extraConfig,
670
+ if ( pageType === PAGE_TYPES . APP ) {
671
+ if ( isClientComponent ) {
672
+ if ( generateStaticParams ) {
673
+ throw new Error (
674
+ `Page "${ page } " cannot use both "use client" and export function "generateStaticParams()".`
675
+ )
676
+ }
677
+
678
+ // Discover if any app configurations are provided on the component. If
679
+ // any are, we need to error because the configuration is not used.
680
+ if ( extraProperties ) {
681
+ for ( const key of AppSegmentConfigSchemaKeys ) {
682
+ if ( ! extraProperties . has ( key ) ) continue
683
+
684
+ throw new Error (
685
+ `Page "${ page } " cannot use both "use client" and export "${ key } "`
686
+ )
687
+ }
688
+ }
669
689
}
670
690
}
671
691
672
692
return {
673
- ssr : false ,
674
- ssg : false ,
675
- rsc : RSC_MODULE_TYPES . server ,
676
- generateStaticParams : false ,
677
- generateImageMetadata : false ,
678
- generateSitemaps : false ,
679
- amp : false ,
680
- runtime : undefined ,
693
+ ssr,
694
+ ssg,
695
+ rsc,
696
+ generateStaticParams,
697
+ generateImageMetadata,
698
+ generateSitemaps,
699
+ amp : config . amp || false ,
700
+ ...( middlewareConfig && { middleware : middlewareConfig } ) ,
701
+ ...( resolvedRuntime && { runtime : resolvedRuntime } ) ,
702
+ preferredRegion,
703
+ extraConfig,
681
704
}
682
705
}
0 commit comments