1
+ use std:: sync:: LazyLock ;
2
+
1
3
use anyhow:: Result ;
2
- use turbo_rcstr:: rcstr;
4
+ use regex:: Regex ;
5
+ use turbo_rcstr:: { RcStr , rcstr} ;
3
6
use turbo_tasks:: { ResolvedVc , Vc } ;
4
7
use turbo_tasks_fs:: { self , FileSystemEntryType , FileSystemPath } ;
5
- use turbopack:: module_options:: { LoaderRuleItem , OptionWebpackRules , WebpackRules } ;
8
+ use turbopack:: module_options:: LoaderRuleItem ;
6
9
use turbopack_core:: {
7
10
issue:: { Issue , IssueExt , IssueSeverity , IssueStage , OptionStyledString , StyledString } ,
8
11
reference_type:: { CommonJsReferenceSubType , ReferenceType } ,
9
12
resolve:: { node:: node_cjs_resolve_options, parse:: Request , pattern:: Pattern , resolve} ,
10
13
} ;
11
14
use turbopack_node:: transforms:: webpack:: WebpackLoaderItem ;
12
15
16
+ // https://babeljs.io/docs/config-files
17
+ // TODO: Also support a `babel` key in a package.json file
13
18
const BABEL_CONFIG_FILES : & [ & str ] = & [
14
19
".babelrc" ,
15
20
".babelrc.json" ,
@@ -22,94 +27,71 @@ const BABEL_CONFIG_FILES: &[&str] = &[
22
27
"babel.config.cjs" ,
23
28
] ;
24
29
25
- /// If the user has a babel configuration file (see list above) alongside their
26
- /// `next.config.js` configuration, automatically add `babel-loader` as a
27
- /// webpack loader for each eligible file type if it doesn't already exist.
28
- #[ turbo_tasks:: function]
29
- pub async fn maybe_add_babel_loader (
30
- project_root : FileSystemPath ,
31
- webpack_rules : Option < ResolvedVc < WebpackRules > > ,
32
- ) -> Result < Vc < OptionWebpackRules > > {
33
- let has_babel_config = {
34
- let mut has_babel_config = false ;
35
- for & filename in BABEL_CONFIG_FILES {
36
- let filetype = * project_root. join ( filename) ?. get_type ( ) . await ?;
37
- if matches ! ( filetype, FileSystemEntryType :: File ) {
38
- has_babel_config = true ;
39
- break ;
40
- }
41
- }
42
- has_babel_config
43
- } ;
30
+ static BABEL_LOADER_RE : LazyLock < Regex > =
31
+ LazyLock :: new ( || Regex :: new ( r"(^|/)@?babel[-/]loader($|/|\.)" ) . unwrap ( ) ) ;
44
32
45
- if has_babel_config {
46
- let mut rules = if let Some ( webpack_rules) = webpack_rules {
47
- webpack_rules. owned ( ) . await ?
48
- } else {
49
- Default :: default ( )
50
- } ;
51
- let mut has_emitted_babel_resolve_issue = false ;
52
- let mut has_changed = false ;
53
- for pattern in [ "*.js" , "*.jsx" , "*.ts" , "*.tsx" , "*.cjs" , "*.mjs" ] {
54
- let rule = rules. get_mut ( pattern) ;
55
- let has_babel_loader = if let Some ( rule) = rule. as_ref ( ) {
56
- rule. loaders
57
- . await ?
58
- . iter ( )
59
- . any ( |c| c. loader == "babel-loader" )
60
- } else {
61
- false
62
- } ;
63
-
64
- if !has_babel_loader {
65
- if !has_emitted_babel_resolve_issue
66
- && !* is_babel_loader_available ( project_root. clone ( ) ) . await ?
67
- {
68
- BabelIssue {
69
- path : project_root. clone ( ) ,
70
- title : StyledString :: Text ( rcstr ! (
71
- "Unable to resolve babel-loader, but a babel config is present"
72
- ) )
73
- . resolved_cell ( ) ,
74
- description : StyledString :: Text ( rcstr ! (
75
- "Make sure babel-loader is installed via your package manager."
76
- ) )
77
- . resolved_cell ( ) ,
78
- severity : IssueSeverity :: Fatal ,
79
- }
80
- . resolved_cell ( )
81
- . emit ( ) ;
82
-
83
- has_emitted_babel_resolve_issue = true ;
84
- }
33
+ pub async fn detect_likely_babel_loader (
34
+ webpack_rules : & [ ( RcStr , LoaderRuleItem ) ] ,
35
+ ) -> Result < Option < RcStr > > {
36
+ for ( glob, rule) in webpack_rules {
37
+ if rule
38
+ . loaders
39
+ . await ?
40
+ . iter ( )
41
+ . any ( |item| BABEL_LOADER_RE . is_match ( & item. loader ) )
42
+ {
43
+ return Ok ( Some ( glob. clone ( ) ) ) ;
44
+ }
45
+ }
46
+ Ok ( None )
47
+ }
85
48
86
- let loader = WebpackLoaderItem {
87
- loader : rcstr ! ( "babel-loader" ) ,
88
- options : Default :: default ( ) ,
89
- } ;
90
- if let Some ( rule) = rule {
91
- let mut loaders = rule. loaders . owned ( ) . await ?;
92
- loaders. push ( loader) ;
93
- rule. loaders = ResolvedVc :: cell ( loaders) ;
94
- } else {
95
- rules. insert (
96
- pattern. into ( ) ,
97
- LoaderRuleItem {
98
- loaders : ResolvedVc :: cell ( vec ! [ loader] ) ,
99
- rename_as : Some ( rcstr ! ( "*" ) ) ,
100
- condition : None ,
101
- } ,
102
- ) ;
103
- }
104
- has_changed = true ;
105
- }
49
+ /// If the user has a babel configuration file (see list above) alongside their `next.config.js`
50
+ /// configuration, automatically add `babel-loader` as a webpack loader for each eligible file type
51
+ /// if it doesn't already exist.
52
+ pub async fn get_babel_loader_rules (
53
+ project_root : FileSystemPath ,
54
+ ) -> Result < Vec < ( RcStr , LoaderRuleItem ) > > {
55
+ let mut has_babel_config = false ;
56
+ for & filename in BABEL_CONFIG_FILES {
57
+ let filetype = * project_root. join ( filename) ?. get_type ( ) . await ?;
58
+ if matches ! ( filetype, FileSystemEntryType :: File ) {
59
+ has_babel_config = true ;
60
+ break ;
106
61
}
62
+ }
63
+ if !has_babel_config {
64
+ return Ok ( Vec :: new ( ) ) ;
65
+ }
107
66
108
- if has_changed {
109
- return Ok ( Vc :: cell ( Some ( ResolvedVc :: cell ( rules) ) ) ) ;
67
+ if !* is_babel_loader_available ( project_root. clone ( ) ) . await ? {
68
+ BabelIssue {
69
+ path : project_root. clone ( ) ,
70
+ title : StyledString :: Text ( rcstr ! (
71
+ "Unable to resolve babel-loader, but a babel config is present"
72
+ ) )
73
+ . resolved_cell ( ) ,
74
+ description : StyledString :: Text ( rcstr ! (
75
+ "Make sure babel-loader is installed via your package manager."
76
+ ) )
77
+ . resolved_cell ( ) ,
78
+ severity : IssueSeverity :: Fatal ,
110
79
}
80
+ . resolved_cell ( )
81
+ . emit ( ) ;
111
82
}
112
- Ok ( Vc :: cell ( webpack_rules) )
83
+
84
+ Ok ( vec ! [ (
85
+ rcstr!( "*.{js,jsx,ts,tsx,cjs,mjs,mts,cts}" ) ,
86
+ LoaderRuleItem {
87
+ loaders: ResolvedVc :: cell( vec![ WebpackLoaderItem {
88
+ loader: rcstr!( "babel-loader" ) ,
89
+ options: Default :: default ( ) ,
90
+ } ] ) ,
91
+ rename_as: Some ( rcstr!( "*" ) ) ,
92
+ condition: None ,
93
+ } ,
94
+ ) ] )
113
95
}
114
96
115
97
#[ turbo_tasks:: function]
0 commit comments