1
1
use std:: sync:: LazyLock ;
2
2
3
- use anyhow:: Result ;
3
+ use anyhow:: { Context , Result } ;
4
4
use regex:: Regex ;
5
5
use turbo_rcstr:: { RcStr , rcstr} ;
6
- use turbo_tasks:: { ResolvedVc , Vc } ;
7
- use turbo_tasks_fs:: { self , FileSystemEntryType , FileSystemPath } ;
6
+ use turbo_tasks:: ResolvedVc ;
7
+ use turbo_tasks_fs:: { self , FileSystemEntryType , FileSystemPath , to_sys_path } ;
8
8
use turbopack:: module_options:: { ConditionItem , LoaderRuleItem } ;
9
- use turbopack_core:: {
10
- issue:: { Issue , IssueExt , IssueSeverity , IssueStage , OptionStyledString , StyledString } ,
11
- reference_type:: { CommonJsReferenceSubType , ReferenceType } ,
12
- resolve:: { node:: node_cjs_resolve_options, parse:: Request , pattern:: Pattern , resolve} ,
13
- } ;
14
9
use turbopack_node:: transforms:: webpack:: WebpackLoaderItem ;
15
10
16
11
use crate :: next_shared:: webpack_rules:: WebpackLoaderBuiltinCondition ;
@@ -32,6 +27,10 @@ const BABEL_CONFIG_FILES: &[&str] = &[
32
27
static BABEL_LOADER_RE : LazyLock < Regex > =
33
28
LazyLock :: new ( || Regex :: new ( r"(^|/)@?babel[-/]loader($|/|\.)" ) . unwrap ( ) ) ;
34
29
30
+ /// The forked version of babel-loader that we should use for automatic configuration. This version
31
+ /// is always available, as it's installed as part of next.js.
32
+ const NEXT_JS_BABEL_LOADER : & str = "next/dist/build/babel/loader" ;
33
+
35
34
pub async fn detect_likely_babel_loader (
36
35
webpack_rules : & [ ( RcStr , LoaderRuleItem ) ] ,
37
36
) -> Result < Option < RcStr > > {
@@ -54,41 +53,44 @@ pub async fn detect_likely_babel_loader(
54
53
pub async fn get_babel_loader_rules (
55
54
project_root : FileSystemPath ,
56
55
) -> Result < Vec < ( RcStr , LoaderRuleItem ) > > {
57
- let mut has_babel_config = false ;
56
+ let mut babel_config_path = None ;
58
57
for & filename in BABEL_CONFIG_FILES {
59
- let filetype = * project_root. join ( filename) ?. get_type ( ) . await ?;
58
+ let path = project_root. join ( filename) ?;
59
+ let filetype = * path. get_type ( ) . await ?;
60
60
if matches ! ( filetype, FileSystemEntryType :: File ) {
61
- has_babel_config = true ;
61
+ babel_config_path = Some ( path ) ;
62
62
break ;
63
63
}
64
64
}
65
- if !has_babel_config {
65
+ let Some ( babel_config_path ) = babel_config_path else {
66
66
return Ok ( Vec :: new ( ) ) ;
67
- }
67
+ } ;
68
68
69
- if !* is_babel_loader_available ( project_root. clone ( ) ) . await ? {
70
- BabelIssue {
71
- path : project_root. clone ( ) ,
72
- title : StyledString :: Text ( rcstr ! (
73
- "Unable to resolve babel-loader, but a babel config is present"
74
- ) )
75
- . resolved_cell ( ) ,
76
- description : StyledString :: Text ( rcstr ! (
77
- "Make sure babel-loader is installed via your package manager."
78
- ) )
79
- . resolved_cell ( ) ,
80
- severity : IssueSeverity :: Fatal ,
81
- }
82
- . resolved_cell ( )
83
- . emit ( ) ;
84
- }
69
+ // - See `packages/next/src/build/babel/loader/types.d.ts` for all the configuration options.
70
+ // - See `packages/next/src/build/get-babel-loader-config.ts` for how we use this in webpack.
71
+ let mut loader_options = serde_json:: Map :: new ( ) ;
72
+
73
+ // `transformMode: default` (what the webpack implementation does) would run all of the
74
+ // Next.js-specific transforms as babel transforms. Because we always have to pay the cost
75
+ // of parsing with SWC after the webpack loader runs, we want to keep running those
76
+ // transforms using SWC, so use `standalone` instead.
77
+ loader_options. insert ( "transformMode" . to_owned ( ) , "standalone" . into ( ) ) ;
78
+
79
+ loader_options. insert (
80
+ "cwd" . to_owned ( ) ,
81
+ to_sys_path_str ( project_root) . await ?. into ( ) ,
82
+ ) ;
83
+ loader_options. insert (
84
+ "configFile" . to_owned ( ) ,
85
+ to_sys_path_str ( babel_config_path) . await ?. into ( ) ,
86
+ ) ;
85
87
86
88
Ok ( vec ! [ (
87
89
rcstr!( "*.{js,jsx,ts,tsx,cjs,mjs,mts,cts}" ) ,
88
90
LoaderRuleItem {
89
91
loaders: ResolvedVc :: cell( vec![ WebpackLoaderItem {
90
- loader: rcstr!( "babel-loader" ) ,
91
- options: Default :: default ( ) ,
92
+ loader: rcstr!( NEXT_JS_BABEL_LOADER ) ,
93
+ options: loader_options ,
92
94
} ] ) ,
93
95
rename_as: Some ( rcstr!( "*" ) ) ,
94
96
condition: Some ( ConditionItem :: Not ( Box :: new( ConditionItem :: Builtin (
@@ -98,49 +100,13 @@ pub async fn get_babel_loader_rules(
98
100
) ] )
99
101
}
100
102
101
- #[ turbo_tasks:: function]
102
- pub async fn is_babel_loader_available ( project_path : FileSystemPath ) -> Result < Vc < bool > > {
103
- let result = resolve (
104
- project_path. clone ( ) ,
105
- ReferenceType :: CommonJs ( CommonJsReferenceSubType :: Undefined ) ,
106
- Request :: parse ( Pattern :: Constant ( rcstr ! ( "babel-loader/package.json" ) ) ) ,
107
- node_cjs_resolve_options ( project_path) ,
108
- ) ;
109
- let assets = result. primary_sources ( ) . await ?;
110
- Ok ( Vc :: cell ( !assets. is_empty ( ) ) )
111
- }
112
-
113
- #[ turbo_tasks:: value]
114
- struct BabelIssue {
115
- path : FileSystemPath ,
116
- title : ResolvedVc < StyledString > ,
117
- description : ResolvedVc < StyledString > ,
118
- severity : IssueSeverity ,
119
- }
120
-
121
- #[ turbo_tasks:: value_impl]
122
- impl Issue for BabelIssue {
123
- #[ turbo_tasks:: function]
124
- fn stage ( & self ) -> Vc < IssueStage > {
125
- IssueStage :: Transform . into ( )
126
- }
127
-
128
- fn severity ( & self ) -> IssueSeverity {
129
- self . severity
130
- }
131
-
132
- #[ turbo_tasks:: function]
133
- fn file_path ( & self ) -> Vc < FileSystemPath > {
134
- self . path . clone ( ) . cell ( )
135
- }
136
-
137
- #[ turbo_tasks:: function]
138
- fn title ( & self ) -> Vc < StyledString > {
139
- * self . title
140
- }
141
-
142
- #[ turbo_tasks:: function]
143
- fn description ( & self ) -> Vc < OptionStyledString > {
144
- Vc :: cell ( Some ( self . description ) )
145
- }
103
+ /// A system path that can be passed to the webpack loader
104
+ async fn to_sys_path_str ( path : FileSystemPath ) -> Result < String > {
105
+ let sys_path = to_sys_path ( path)
106
+ . await ?
107
+ . context ( "path should use a DiskFileSystem" ) ?;
108
+ Ok ( sys_path
109
+ . to_str ( )
110
+ . with_context ( || "{sys_path:?} is not valid utf-8" ) ?
111
+ . to_owned ( ) )
146
112
}
0 commit comments