11use std:: iter;
22
33use anyhow:: Result ;
4+ use either:: Either ;
45use indoc:: formatdoc;
56use itertools:: Itertools ;
67use turbo_tasks:: { ResolvedVc , Vc } ;
7- use turbo_tasks_fs:: FileContent ;
8+ use turbo_tasks_fs:: { FileContent , FileSystemPath } ;
89use turbopack_core:: {
910 asset:: AssetContent ,
10- resolve:: { ResolveResult , options:: ImportMapping } ,
11+ resolve:: {
12+ ResolveResult ,
13+ options:: { ImportMap , ImportMapping } ,
14+ } ,
1115 virtual_source:: VirtualSource ,
1216} ;
1317
14- use crate :: { app_structure:: CollectedRootParams , embed_js:: next_js_file_path} ;
18+ use crate :: {
19+ app_structure:: CollectedRootParams ,
20+ embed_js:: next_js_file_path,
21+ next_client:: ClientContextType ,
22+ next_server:: ServerContextType ,
23+ next_shared:: resolve:: { InvalidImportPattern , InvalidImportResolvePlugin } ,
24+ } ;
25+
26+ pub fn get_invalid_next_root_params_resolve_plugin (
27+ is_root_params_enabled : bool ,
28+ ty : Either < ServerContextType , ClientContextType > ,
29+ root : ResolvedVc < FileSystemPath > ,
30+ ) -> Option < Vc < InvalidImportResolvePlugin > > {
31+ // Hard-error if the flag is not enabled, regardless of if we're on the server or on the client.
32+ if !is_root_params_enabled {
33+ return Some ( InvalidImportResolvePlugin :: new (
34+ * root,
35+ InvalidImportPattern :: Glob ( "next/root-params" . into ( ) ) ,
36+ vec ! [
37+ "'next/root-params' can only be imported when `experimental.rootParams` is \
38+ enabled."
39+ . into( ) ,
40+ ] ,
41+ ) ) ;
42+ }
43+ match ty {
44+ Either :: Left ( server_ty) => match server_ty {
45+ ServerContextType :: AppRSC { .. } | ServerContextType :: AppRoute { .. } => {
46+ // Valid usage. We'll map this request to generated code in
47+ // `insert_next_root_params_mapping`.
48+ None
49+ }
50+ ServerContextType :: PagesData { .. }
51+ | ServerContextType :: PagesApi { .. }
52+ | ServerContextType :: Instrumentation { .. }
53+ | ServerContextType :: Middleware { .. } => {
54+ // There's no sensible way to use root params outside of the app directory.
55+ // TODO: make sure this error is consistent with webpack
56+ Some ( InvalidImportResolvePlugin :: new (
57+ * root,
58+ InvalidImportPattern :: Glob ( "next/root-params" . into ( ) ) ,
59+ vec ! [ "'next/root-params' can only be used inside the App Directory." . into( ) ] ,
60+ ) )
61+ }
62+ _ => {
63+ // In general, the compiler should prevent importing 'next/root-params' from client
64+ // modules, but it doesn't catch everything. If an import slips
65+ // through our validation, make it error.
66+ Some ( InvalidImportResolvePlugin :: new (
67+ * root,
68+ InvalidImportPattern :: Glob ( "next/root-params" . into ( ) ) ,
69+ vec ! [
70+ "'next/root-params' cannot be imported from a Client Component module. It \
71+ should only be used from a Server Component."
72+ . into( ) ,
73+ ] ,
74+ ) )
75+ }
76+ } ,
77+ Either :: Right ( _) => {
78+ // In general, the compiler should prevent importing 'next/root-params' from client
79+ // modules, but it doesn't catch everything. If an import slips
80+ // through our validation, make it error.
81+ Some ( InvalidImportResolvePlugin :: new (
82+ * root,
83+ InvalidImportPattern :: Glob ( "next/root-params" . into ( ) ) ,
84+ vec ! [
85+ "'next/root-params' cannot be imported from a Client Component module. It \
86+ should only be used from a Server Component."
87+ . into( ) ,
88+ ] ,
89+ ) )
90+ }
91+ }
92+ }
93+
94+ pub async fn insert_next_root_params_mapping (
95+ import_map : & mut ImportMap ,
96+ ty : ServerContextType ,
97+ collected_root_params : Option < Vc < CollectedRootParams > > ,
98+ ) -> Result < ( ) > {
99+ match ty {
100+ ServerContextType :: AppRSC { .. } | ServerContextType :: AppRoute { .. } => {
101+ import_map. insert_exact_alias (
102+ "next/root-params" ,
103+ get_next_root_params_mapping ( collected_root_params)
104+ . to_resolved ( )
105+ . await ?,
106+ ) ;
107+ }
108+ _ => {
109+ // `get_invalid_next_root_params_resolve_plugin` already triggered an error for other
110+ // contexts, so we can ignore them here.
111+ }
112+ } ;
113+ Ok ( ( ) )
114+ }
15115
16116#[ turbo_tasks:: function]
17- pub async fn get_next_root_params_mapping (
117+ async fn get_next_root_params_mapping (
18118 collected_root_params : Option < Vc < CollectedRootParams > > ,
19119) -> Result < Vc < ImportMapping > > {
20120 let module_content = match collected_root_params {
@@ -30,24 +130,24 @@ pub async fn get_next_root_params_mapping(
30130 . chain ( collected_root_params. iter ( ) . map ( |param_name| {
31131 formatdoc ! (
32132 r#"
33- export function {param_name }() {{
34- return getRootParam('{param_name }');
133+ export function {PARAM_NAME }() {{
134+ return getRootParam('{PARAM_NAME }');
35135 }}
36136 "# ,
37- param_name = param_name,
137+ PARAM_NAME = param_name,
38138 )
39139 } ) )
40140 . join ( "\n " )
41141 }
42142 } ;
43143
44- let js_asset = VirtualSource :: new (
144+ let source = VirtualSource :: new (
45145 next_js_file_path ( "root-params.js" . into ( ) ) ,
46146 AssetContent :: file ( FileContent :: Content ( module_content. into ( ) ) . cell ( ) ) ,
47147 )
48148 . to_resolved ( )
49149 . await ?;
50150
51- let mapping = ImportMapping :: Direct ( ResolveResult :: source ( ResolvedVc :: upcast ( js_asset ) ) ) ;
151+ let mapping = ImportMapping :: Direct ( ResolveResult :: source ( ResolvedVc :: upcast ( source ) ) ) ;
52152 Ok ( mapping. cell ( ) )
53153}
0 commit comments